`
543089122
  • 浏览: 153793 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

关于Swing打印二叉树

 
阅读更多
[img][/img]最近研究二叉树一类的树结构,总用调试的办法去搞好麻烦,想用界面显示树结构,想看是不是AVL啊,(红黑树那个还在弄,太麻烦),于是就想弄出界面来,考虑了下准备用flex做也网页图形的,毕竟研究过用flex绘图等简单的东西,又想想swing这东西还一直没弄过呢,学java的不会swing惭愧啊,就是今天给按钮加个点击事件都纠结了半天。。。。唉 性格所致 ,越是不会的东西越要去折腾。反正也是做的玩玩,于是就用swing去折腾,虽然我从没用过,但我想还不是一个绘图类画画直线啥的。

关于绘制树的思路:从最简单的二叉树搞起。每个节点增加坐标属性(x,y),然后 遍历树的话也就是那4种遍历方法。前序、中序、后序、层次,或许有其他的遍历方法,目前只知道这几种简单的方法来遍历树。

下面上代码,代码写的很随意,未整理。
package sunfa.tree;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

public class TreeXY<K, V> {
	public static void main(String[] args) {
		TreeXY<String, Object> tree = new TreeXY<String, Object>(null,
				new BaseXY(600, 0, 20, 10));
		int n = 10;
		Random ran = new Random();
		for (int i = 0; i < n; i++) {
			int o = ran.nextInt(100);
			System.out.print(o + ",");
			tree.put(Integer.toString(o), o);
		}
		System.out.println("printTree:");
		tree.printTree(tree.root);
	}

	private Comparator<K> comp;
	private Entry<K, V> root;
	private BaseXY baseXY;

	public TreeXY(Comparator<K> c, BaseXY baseXY) {
		if (c != null)
			comp = c;
		this.baseXY = baseXY;
	}

	public Entry<K, V> getRoot() {
		return root;
	}

	private List<Entry<K, V>> equalsXY = new ArrayList<Entry<K, V>>();
	private Set<String> xyHash = new HashSet<String>();

	public void printTree(Entry<K, V> node) {
		if (node == null)
			return;
		printTree(node.left);
		System.out.print(node.key+",");// + "[" + node.xy + "],");
		String key = node.xy.x + "_" + node.xy.y;
		if (xyHash.contains(key)) {
			equalsXY.add(node);
		} else {
			xyHash.add(key);
		}
		printTree(node.right);
	}

	public void put(K key, V value) {
		if (root == null) {
			root = new Entry<K, V>(key, value, new XYPosition(baseXY.rootX,
					baseXY.rootY), null);
			return;
		}
		Entry<K, V> node = root;
		while (true) {
			int n = compare(key, node.key);
			if (n == 0) {
				node.value = value;
				return;
			} else if (n < 0) {
				if (node.left != null) {
					node = node.left;
				} else {
					node.left = new Entry<K, V>(key, value, new XYPosition(
							node.xy.x - baseXY.xgap, node.xy.y + baseXY.ygap),
							node);
					return;
				}
			} else {
				if (node.right != null) {
					node = node.right;
				} else {
					node.right = new Entry<K, V>(key, value, new XYPosition(
							node.xy.x + baseXY.xgap, node.xy.y + baseXY.ygap),
							node);
					return;
				}
			}
		}
	}

	private int compare(K k1, K k2) {
		return comp != null ? (comp.compare(k1, k2)) : (((Comparable<K>) k1)
				.compareTo(k2));
	}

	public static class Entry<K, V> {
		public Entry<K, V> parent;
		public Entry<K, V> left;
		public Entry<K, V> right;
		public XYPosition xy;
		public K key;
		public V value;

		public Entry(K key, V value, XYPosition xy, Entry<K, V> parent) {
			super();
			this.key = key;
			this.value = value;
			this.xy = xy;
			this.parent = parent;
		}
	}

	public static class XYPosition {
		public int x;
		public int y;

		public XYPosition(int x, int y) {
			super();
			this.x = x;
			this.y = y;
		}

		public String toString() {
			return this.x + "," + this.y;
		}
	}
}


上面是一颗简单的二叉树,节点含有坐标属性。

package sunfa.tree;

public class BaseXY {
	int rootX;// 根节点的X
	int rootY;// 根节点的Y
	// int size;// 单元格子大小

	// 水平和垂直间距
	int xgap;
	int ygap;

	public BaseXY(int rootX, int rootY, int xgap, int ygap) {
		super();
		this.rootX = rootX;
		this.rootY = rootY;
		this.xgap = xgap;
		this.ygap = ygap;
	}

}


这个是初始化树的时候传递的基础坐标和间距的类。
package sunfa.tree.draw;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Line2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

import sunfa.tree.BaseXY;
import sunfa.tree.TreeXY;
import sunfa.tree.TreeXY.Entry;

public class GrapDemo1 extends JFrame implements ActionListener {
	private static final long serialVersionUID = 1L;
	JButton btn1;
	Container c;

	public GrapDemo1() {
		super("map");
		c = getContentPane();
		setSize(1000, 600);
		MapPanel comp = new MapPanel();
		comp.setSize(400, 400);
		btn1 = new JButton("text");
		btn1.setSize(100, 30);
		// btn1.action(MouseEvent.MOUSE_CLICKED, this);
		btn1.addActionListener(this); // 注册bt的监听者对象this
		getContentPane().add(btn1);
		getContentPane().add(comp);
	}

	public static void main(String[] args) {
		GrapDemo1 frame = new GrapDemo1();
		frame.setVisible(true);
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == btn1) {
			Random ran = new Random();
			btn1.setText("btn1" + ran.nextInt(111));
			MapPanel comp = new MapPanel();
			comp.setSize(400, 400);
			c.add(comp);
		}
	}
}

class MapPanel extends JPanel {
	private static final long serialVersionUID = 1L;

	@Override
	protected void paintComponent(Graphics g) {
		// test(g);
		Graphics2D grap = (Graphics2D) g;// 绘图对象
		grap.clearRect(0, 0, 400, 400);
		// drawGrid(grap);

		// DrawTree();
		// render(400, 400, grap);
		DrawTree(grap);
	}

	private void DrawTree(Graphics2D grap) {
		BufferedImage srcImg = new BufferedImage(800, 800,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D imageG2 = srcImg.createGraphics();
		String text = "aa";
		Font font = new Font("宋体", Font.BOLD, 28);
		FontRenderContext frc = grap.getFontRenderContext();
		TextLayout tl = new TextLayout(text, font, frc);

		// 设置白底黑字
		imageG2.setBackground(Color.WHITE);
		imageG2.clearRect(0, 0, 800, 800);
		imageG2.setColor(Color.BLACK);

		TreeXY<Integer, Object> tree = new TreeXY<Integer, Object>(
				new Comparator<Integer>() {
					public int compare(Integer o1, Integer o2) {
						return o1 - o2;
					}
				}, new BaseXY(400, 30, 100, 40));
		int n = 50;
		list.clear();
		list2.clear();
		Random ran = new Random();
		for (int i = 0; i < n; i++) {
			int o = ran.nextInt(100);
			System.out.print(o + ",");
			tree.put(o, o);
		}
		System.out.println("printTree:");
		// tree.printTree(tree.getRoot());
		// drawTree(grap);
		printTree(tree.getRoot(), imageG2);

		String ss = "";
		for (int i = 0; i < list2.size(); i++) {
			ss += list2.get(i).toString() + ",";
		}
		imageG2.drawString(ss, 10, 20);

		tl.draw(imageG2, 0, 0);
		grap.drawImage(srcImg, 100, 10, srcImg.getWidth(), srcImg.getHeight(),
				null);
	}

	public void render(int w, int h, Graphics2D g2) {
		BufferedImage srcImg = new BufferedImage(w, h,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D imageG2 = srcImg.createGraphics();

		// 设置白底黑字
		imageG2.setBackground(Color.RED);
		imageG2.clearRect(0, 0, w, h);
		imageG2.setColor(Color.BLACK);

		String text = "标题:订货单位";
		Font font = new Font("宋体", Font.BOLD, 28);
		FontRenderContext frc = g2.getFontRenderContext();
		TextLayout tl = new TextLayout(text, font, frc);
		tl.draw(imageG2, 50, 50);

		// 文字的宽度将压缩一半显示
		g2.drawImage(srcImg, 0, 0, srcImg.getWidth() / 2, srcImg.getHeight(),
				null);
		// 文字正常显示
		g2.drawImage(srcImg, 0, srcImg.getHeight(), srcImg.getWidth(),
				srcImg.getHeight(), null);
	}

	int n = 10;

	private void drawTree(Graphics2D grap) {
		if (n == 0) {
			return;
		}
		grap.drawArc(100, 200, 110, 110, 0, 360);
		n--;
		drawTree(grap);
	}

	// private List<TreeXY.Entry> equalsXY = new ArrayList<TreeXY.Entry>();
	// private Set<String> xyHash = new HashSet<String>();
	private List<String> list = new ArrayList<String>();
	private List<String> list2 = new ArrayList<String>();

	// 中序周游打印二叉树
	public void printTree(TreeXY.Entry node, Graphics2D grap) {
		if (node == null)
			return;
		printTree(node.left, grap);
		// ---------start
		String key = node.xy.x + "_" + node.xy.y;
		int r = 30;
		// int tmp = node.xy.x - r;
		boolean isExits = false;
		for (int i = 0; i < list.size(); i++) {
			if (list.get(i).toString().equals(key)) {
				isExits = true;
			}
		}
		if (isExits) {
			// node.xy.x += 40;
			// tmp += 20;
			System.out.print("存在:" + key + ",");
		} else {
			list.add(key);
		}
		// if (xyHash.contains(key)) {
		// // equalsXY.add(node);
		// node.xy.x +=40;
		// tmp += 20;
		// System.out.print("存在:"+key+",");
		// } else {
		// xyHash.add(key);
		// }
		int circleX = node.xy.x + r / 2 - 5;// 圆心
		int circleY = node.xy.y + r / 2 + 5;
		grap.drawArc(node.xy.x, node.xy.y, r, r, 0, 360);
		grap.drawString(node.key.toString(), circleX, circleY);
		if (node.parent != null) {
			int circleX2 = node.parent.xy.x + r - 5;
			int circleY2 = node.parent.xy.y + r + -5 - r / 2;
			grap.drawLine(circleX2, circleY2, circleX, circleY);
		}
		System.out.print(node.key + "[" + node.xy + "],");
		list2.add(node.key.toString());
		// -------------end
		printTree(node.right, grap);
	}

	private Queue<TreeXY.Entry> queue = new ArrayBlockingQueue<TreeXY.Entry>(10);

	// 层次打印二叉树
	public void printTree2(TreeXY.Entry node, Graphics2D grap) {
		queue.offer(node);
		while (queue.size() > 0) {
			node = queue.poll();
			if (node == null) {
				System.out.println("结束");
				return;
			}
			String key = node.xy.x + "_" + node.xy.y;
			int r = 30;
			// int tmp = node.xy.x - r;
			boolean isExits = false;
			for (int i = 0; i < list.size(); i++) {
				if (list.get(i).toString().equals(key)) {
					isExits = true;
				}
			}
			if (isExits) {
				node.xy.x += 40;
				// tmp += 20;
				System.out.print("存在:" + key + ",");
			} else {
				list.add(key);
			}
			int circleX = node.xy.x + r / 2 - 5;// 圆心
			int circleY = node.xy.y + r / 2 + 5;
			grap.drawArc(node.xy.x, node.xy.y, r, r, 0, 360);
			grap.drawString(node.key.toString(), circleX, circleY);
			if (node.parent != null) {
				int circleX2 = node.parent.xy.x + r - 5;
				int circleY2 = node.parent.xy.y + r + -5 - r / 2;
				grap.drawLine(circleX2, circleY2, circleX, circleY);
			}
			if (node.left != null)
				queue.offer(node.left);
			if (node.right != null)
				queue.offer(node.right);
		}
	}

	private void printTree2_0(ArrayBlockingQueue<TreeXY.Entry> queue,
			Graphics2D grap) {
		while (queue.size() > 0) {
			Entry o = queue.poll();
			if (o == null) {
				break;
			}
			if (o.left != null)
				queue.offer(o.left);
			if (o.right != null)
				queue.offer(o.right);
		}
	}

	// 绘制表格
	private void drawGrid(Graphics grap) {
		// grap = (Graphics2D) g;// 绘图对象
		int x1 = 0, y1 = 0, x2 = 0, y2 = 0, size = 40, row = 14, col = 17;
		int x3 = 0, y3 = 0;// 圆的坐标
		for (int i = 0; i < row; i++) {
			grap.drawLine(x1, y1, (col - 1) * size, y1);

			x3 = x1 + size >>> 1;
			y3 = y1 + size - (size >>> 1);
			grap.drawString((x1 + size >>> 1) + ","
					+ (y1 + size - (size >>> 1)), x3, y3);

			// 绘制的圆是按坐标点绘制弧形成圆的,无法得到圆心坐标点...
			grap.drawArc(x3, y3, size, size, 0, 360);
			y1 += size;
		}
		y1 = 0;
		for (int i = 0; i < col; i++) {
			grap.drawLine(x1, y1, x1, (row - 1) * size);
			x1 += size;
		}

		System.out.println("grap:" + grap);
	}

	// 学习笔记
	private void test(Graphics g) {
		Graphics2D grap = (Graphics2D) g;// 绘图对象
		grap.setPaint(new Color(255));// 设置绘图颜色
		grap.setStroke(new BasicStroke(15));// 设置笔的粗细

		// 在指定坐标处写文字
		grap.drawString("java绘图", 40, 240);
		// 线条
		Line2D.Float line = new Line2D.Float(100.0f, 100.0f, 200.0f, 200.0f);
		grap.draw(line);// 所有的图形都需要添加到绘图对象中才能显示到界面上

		// 矩形
		RoundRectangle2D.Double round = new RoundRectangle2D.Double(150, 140,
				100, 100, 1f, 1f);
		grap.setStroke(new BasicStroke(2));
		grap.draw(round);

		grap.setStroke(new BasicStroke(10));
		// 绘制一个覆盖指定矩形的圆弧或椭圆弧边框。 弧度可以指定从0-360,那么这就是一个圆
		grap.drawArc(20, 20, 20, 20, 0, 360);
		// 调用继承于Graphics类的方法绘制基本的图形,比如直线、矩形区域等
		grap.drawLine(10, 10, 120, 20);
	}
}


这个是绘制二叉树的绘图类,绘图类的代码比较乱没整理,现在也懒得去整理了,因为遇到了一个问题,纠结之。

这张图就是最后绘制出来的效果,很遗憾的是存在交叉的地方,重叠了。虽然想了好几种方法去解决,但都没有好的效果,一下午的时间就在那折腾这个,还好没啥子任务要做。

关于交叉部分的处理办法:
方法一:打印的时候处理。尝试过,效果差还很麻烦,并且和打印的代码混到了一起以后不好分离出来。
方法二:添加节点的时候处理。成功添加一个节点后,拿新添加的节点和其他所有的节点的坐标比较(可以把树的节点左边放到一个hashmap中,这里就直接get了,不用遍历树了),如果存在重叠,这个时候注意观察,怎样才能让他们不重叠呢?办法其实很简单,先搞清楚新添加的节点相对于根节点而言是在根节点的左边还是在根节点的右边。如果是在根节点的左边就使根节点的左子节点向左移动(右边就使根节点的右子节点向右移动),具体平移多少,示打印效果而定,可以平移一个单位或二个单位等等。

写这篇文章的时候我就在想,要不要使根节点的孙子节点也做对应的移动?还是不需要了,如果树的层次多的话,电脑的屏幕也装不下,也就失去了意义了,一般10来个层的树也就比较大了。所以使根节点的左右子树平移已经能满足基本的需求了。


或许也可以做成类似于企业人员结构图的形式。

未完。。。
分享到:
评论

相关推荐

    二叉树 单文档 (数据结构上机作业)

    1. GUI框架:可能需要使用如Qt、wxWidgets、Java Swing或.NET Windows Forms等图形库来创建用户界面。 2. 事件处理:当用户在界面上执行操作(如点击按钮)时,需要捕获和处理这些事件。这可能涉及到添加监听器和...

    2021-2022计算机二级等级考试试题及答案No.13294.docx

    2. 打印范围:在打印命令对话框中,用户可以设置打印的范围,包括整个文档、选定页、页码范围等,因此“打印范围不属于打印命令对话框里设置的内容”是错误的。 3. Java语言中的软件重用:Java通过平台无关性、Java...

    2021-2022计算机二级等级考试试题及答案No.3179.docx

    10. **二叉树遍历**:后序遍历二叉树的顺序是左子树-右子树-根节点,所以给定的二叉树后序遍历结果为DBEFCA。 11. **超链接数据类型**:超链接数据类型字段存储的是指向WEB页或其他文档的链接地址。 12. **应用...

    2021-2022计算机二级等级考试试题及答案No.17437.docx

    26. 打印表格线:当表格线显示为浅灰色时,实际打印可能会根据打印设置决定是否打印出来,可以设置为打印表格线。 以上是对计算机二级等级考试部分试题的知识点详解,涵盖了HTML、JavaScript、数据库管理、计算机...

    2021-2022计算机二级等级考试试题及答案No.17434.docx

    6. Java Swing 组件:在Java Swing中,布局管理器用于控制组件在容器中的排列方式。`JDialog`对象可以设置布局管理器,而其他选项如`JScrollBar`、`JMenuBar`和`JComboBox`通常由特定的默认布局管理。 7. Word 文档...

    2016年计算机二级JAVA考试题及答案.pdf

    20. Swing组件:JFrame、JTable和Menu是Swing的组件,而JPanel属于AWT组件,但Swing可以兼容AWT,所以JPanel也可以看作Swing组件的一部分,选项A不正确。 21. Java程序执行:程序将字符'a'(97)加到整数100上,...

    2021-2022计算机二级等级考试试题及答案No.17902.docx

    8. 显示或打印汉字时,系统使用的是汉字的字形码,字形码是用于显示和打印的点阵或轮廓图形代码。 9. Python的turtle库中,home()函数将画笔位置设为原点,方向朝东;seth(x)是setheading(x)的别名,用于设置画笔的...

    2021-2022计算机二级等级考试试题及答案No.17173.docx

    10. **Swing组件和布局管理器**:在Java Swing中,JDialog对象可以指定布局管理器,而JScrollBar、JMenuBar和JComboBox对象的布局通常是固定的。 11. **跳出循环的命令**:在某些编程语言中,`LOOP`命令通常不会...

    2021-2022计算机二级等级考试试题及答案No.11018.docx

    3. **Word2003打印预览**:在Word2003的打印预览视图下,用户可以通过放大镜按钮切换预览状态与编辑状态,以便更好地查看文档的打印效果。 4. **表格控件**:表格控件允许显示数据源,如表、视图或查询,并且可以...

    2021-2022计算机二级等级考试试题及答案No.16425.docx

    16. Swing组件:JFrame是Swing中创建窗口的主要类,可以用来创建框架窗口。 17. 数据透视表:数据透视表是一种交互式的数据分析工具,允许用户进行定制的计算和汇总。 18. C++目标文件扩展名:C++源代码编译后生成...

    2021-2022计算机二级等级考试试题及答案No.2852.docx

    在二叉树理论中,根据性质,若一个二叉树有435个叶节点,那么度为2的节点数为435 - 1 = 434,因为对于任何非空二叉树,n0 = n2 + 1,其中n0是叶节点数,n2是度为2的节点数,答案是D.434。 磁盘驱动器既是输入设备也...

    2021-2022计算机二级等级考试试题及答案No.18477.docx

    5. 报表输出:报表输出是数据库系统中用于大量数据展示和处理的有效方式,通常用于打印或导出数据,具有良好的格式化和汇总功能。 6. 完全二叉树的叶子节点计算:对于完全二叉树,若总节点数为n,那么叶子节点数为n...

    2021-2022计算机二级等级考试试题及答案No.16714.docx

    - **Component类**:Java Swing中的基类,用于创建GUI组件。 - **子类**:Component类有许多子类,用于实现具体的GUI组件。 **题目解析**: 题目询问哪个组件不是Component的子类。正确答案是D“MenuBar”,因为...

    2021-2022计算机二级等级考试试题及答案No.14169.docx

    9. Swing 容器添加组件:JPanel 是可以直接添加组件的 Swing 容器,其他选项如 JFrame 和 JDialog 通常作为顶级容器,需要先添加 JPanel 进行组件布局。 10. 数据库版本兼容:高版本的 ACCESS 数据库要能在低版本中...

    2021-2022计算机二级等级考试试题及答案No.15559.docx

    25. 打印设置:打印范围是打印命令对话框中可配置的选项之一。 26. 资源管理器:加号和减号表示文件夹的展开和折叠状态。 这些知识点涵盖了计算机基础知识的多个方面,包括网络、操作系统、数据库、编程语言、数据...

    3月全国计算机等级考试二级JAVA笔试试题及答案.pdf

    2. **二叉树的性质**:在二叉树中,如果一个结点的度为2,那么它的子节点中必有一个是叶子结点。因此,度为2的结点数等于叶子结点数减1。题目中提到5个度为2的结点,所以叶子结点数为5+1=6。 3. **排序算法**:冒泡...

    2021-2022计算机二级等级考试试题及答案No.9483.docx

    17. 二叉树遍历:中序遍历二叉树时,通常顺序是左子树-根节点-右子树。 18. Session属性检索:在Java Web开发中,使用session.getAttribute()方法来获取Session属性的值。 19. 集合类:JDK中的集合类能够存储任意...

    2021-2022计算机二级等级考试试题及答案No.1449.docx

    打印范围确实是打印设置中的一项内容。 - **正确选项**: 错误 ### 15. PHP与MySQL交互 - **知识点**: PHP和MySQL之间的数据检索。 - **详细说明**: 在PHP中,`mysql_fetch_array()`、`mysql_fetch_row()`和`mysql_...

    2021-2022计算机二级等级考试试题及答案No.2463.docx

    21. **完全二叉树的叶子节点** - 在完全二叉树中,如果一个节点没有右子节点,那么它必定是叶子节点,因为完全二叉树的叶子节点要么在最后一层,要么在倒数第二层且靠左。 22. **回收站恢复文件** - 通常情况下,从...

Global site tag (gtag.js) - Google Analytics