使用 Java SWT来实现二叉查找树的图形化显示
SWT环境配置: https://blog.csdn.net/u014106644/article/details/88824092
对于二叉查找树的基本操作与数据结构实现可以参考:
二叉查找树BST的基本原理:https://blog.csdn.net/u014106644/article/details/89549048
使用SWT来实现从数组初始化二叉查找树,添加节点以及删除节点等操作的图形化显示,目前功能比较粗糙简单,日后可以继续扩展,实现更多类型数据结构的显示与展示。
二叉树的坐标计算
二叉树的绘制分为节点绘制以及节点之间的连接绘制,这里节点绘制使用Label来实现,连接使用GC对象来实现,节点以及链接均以Group对象为容器。
对于SWT图形坐标系来说,左上角为坐标原点,下面进行坐标点的计算,首先将二叉查找树转换为完全二叉树,这样可以得到最底层节点的个数,绘制时,按照层依次绘制,层与层之间偏移量为labelOffsetY,对于每一层绘制时,节点与上一层的连接线均看成是一个独立的个体,即计算实体总数时,需要考虑节点数与连接线数,将每一层往X轴投影,则可以计算出,每一层的起始偏移量以及节点与节点之间的距离。
首先将二叉查找树按照层序遍历,补全为完全二叉树,空节点使用特殊字符节点来表示,最终结果输出在List<List<Node>>中
public List<List<Node>> levelOrder2(){
if(root==null) return null;
List<List<Node>> ll = new ArrayList<>();
Queue<Node> q = new LinkedList<>();
q.add(root);
int h = height();
int depth = 1;
while(q.size()>0){
int s = q.size();
List<Node> lk = new ArrayList<>();
while(s>0){
Node tmp = q.poll();
lk.add(tmp);
if(tmp.left!=null){
q.add(tmp.left);
}else{
q.add(new Node((Key)specialChar, (Value)specialChar, 1));
}
if(tmp.right!=null){
q.add(tmp.right);
}else{
q.add(new Node((Key)specialChar, (Value)specialChar, 1));
}
s--;
}
ll.add(lk);
if(depth>h-1)
break;
depth++;
}
// for(List<Node> t : ll){
// for(Node n : t){
// System.out.print(n.key + ",");
// }
// System.out.println();
// }
return ll;
}
对于二叉树的节点Node,增加两个变量x,y,其中y表示给节点所在层数,从根节点开始为第一层,x表示该节点在某一层的偏移基本单位个数
public class Node{
private Key key;
private Value value;
private Node left, right;
private int N;
private int x;
private int y;
public Node(Key key, Value value, int N){
this.key = key;
this.value = value;
this.N = N;
}
public Key getKey(){
return this.key;
}
public Node getLeft(){
return this.left;
}
public Node getRight(){
return this.right;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "Node [key=" + key + "]";
}
}
根据层序遍历的结构来计算每个节点的x,y坐标,可以根据特例来寻找每一行起始坐标以及每一行节点之间的偏移值的规律,假定树的总高度为h,h从1开始,则第i行的起始偏移量为
pow2(h-i+1)-2
对于第i行第j个节点,距离起始位置的偏移单位为
pow2(h-i+2)
最终计算如下所示:
public List<List<Node>> calculateNodeCoordinate2(){
int h = height();
List<List<Node>> ll = levelOrder2();
for(int i=1; i<=h; i++){
int lineStart = pow2(h-i+1)-2;
int offset = pow2(h-i+2);
int t = lineStart;
for(int j=0; j<ll.get(i-1).size(); j++){
Node n = ll.get(i-1).get(j);
n.setX(t);
n.setY(i);
//NodeCoordinate nc = new NodeCoordinate(ll.get(i-1).get(j), t, i);
t = t + offset;
}
}
return ll;
}
二叉树的SWT绘制
这里为了便于展示,将绘制起点偏移一下startX,startY
循环,绘制节点
private void showNode(Group group){
ll = bst.calculateNodeCoordinate2();
h = bst.height();
//绘制节点
for(int i=1; i<=h; i++){
for(int j=0; j<ll.get(i-1).size(); j++){
Node nc = ll.get(i-1).get(j);
if(nc.getKey().equals(BSTCoordinate.specialChar))
continue;
Label lname = new Label(group, SWT.NONE|SWT.CENTER);
lname.setBounds(getNodeLabelStartX(nc), getNodeLableStartY(nc), labelW, labelH);
lname.setText(String.valueOf(nc.getKey()));
//System.out.println("i = " + i + " x = " + nc.getX() + " y = " + nc.getY());
}
}
}
对于每个节点的起始x,y坐标计算如下:
private int getNodeLabelStartX(Node n){
return startX + n.getX() * lableOffsetX;
}
private int getNodeLableStartY(Node n){
return startY + n.getY() * lableOffsetY;
}
即对于x坐标,将起始坐标偏移startX,加上改节点的偏移基本单位数乘以每个基本单位的宽度即可
对于y坐标,将每个节点的层数乘以每一层的y偏移即可
绘制连接
首先获取到Group GC对象,设置GC基本属性,开始连接线的绘制,遍历每一个节点,绘制该节点到左右子节点的连接线,坐标计算如下所示:
private void showLink(Group group){
//group.redraw();
//绘制连接
gc = new GC(group);
gc.setLineWidth(1);
for(int i=1; i<=h; i++){
for(int j=0; j<ll.get(i-1).size(); j++){
Node nc = ll.get(i-1).get(j);
if(nc.getLeft()!=null)
gc.drawLine(
getNodeLabelStartX(nc)+labelW/2,
getNodeLableStartY(nc)+labelH,
getNodeLabelStartX(nc.getLeft())+labelW/2,
getNodeLableStartY(nc.getLeft())
);
if(nc.getRight()!=null)
gc.drawLine(
getNodeLabelStartX(nc)+labelW/2,
getNodeLableStartY(nc)+labelH,
getNodeLabelStartX(nc.getRight())+labelW/2,
getNodeLableStartY(nc.getRight())
);
}
}//end for
}
最终显示效果如下所示:
从数组初始化BST显示
添加一部分节点显示
删除一部分节点显示
至此便实现了BST的简单操作以及图形化显示
工程地址:https://github.com/ChenWenKaiVN/TreeSWT
目前只是简单实现显示,在图形化,效率以及异常等方面需要进一步优化,之后学习二叉树的基本操作,可以很方便的来显示操作结果了,下一步优化的方向
1.实现红黑树的增删改查操作的图形化显示。
2.实现操作的实现过程细节显示,例如树的遍历过程等。
3.优化SWT显示部分,加强显示功能。