一种对树状结构的坐标格式化算法

因为工作需要,重新回顾了一年前写的代码,看的时候一点印象也没,老了。。。

树,除了根节点外,所有节点只有一个父节点,称为树。

树,怎样才能称之为好看,拿张草稿纸画了画,得出下述结论:

  1. 按层分隔,层间距一致
  2. 节点连线不重合,即要求同一层中,节点按父节点的顺序摆放。同一层中,节点间距最好满足 1*gap、2*gap、3*gap.....
  3. 父节点的坐标在子节点的中间位置(x轴方向上),对称。

分析及实现

对于第一点

其实很容易,从根节点开始,层次遍历树,当层数变化时,对y轴坐标++,上图即效果。

代码实现方面,不同于一般意义上的层次遍历实现,使用队列或递归实现,其实都会对层数变化无感知,因此,tmpList存放当前层的节点,设置y轴坐标,secondList存放下一层节点,为下次循环做准备。

                tmpList.add(rootNode);
                int y=0;
		while (!tmpList.isEmpty()) {
		        for (NodeTree tmp : tmpList) {
		        	secondList.addAll(tmp.getChildren());//保存下一层的所有节点
    		                tmp.setY(y);//设置层
    			}
        		y++;
        		tmpList.clear();
        		tmpList.addAll(secondList);//用于下次循环
        		secondList.clear();
		}
复制代码

对于第二点

简单做的话,其实每次循环设置x为0,child.setX(x++);即可,但效果如下。

难看,没有对称美。因此需要满足第三点

对于第三点

首先讲一下,在一种特殊情况下,是如何确定x轴坐标的。 套用下满二叉树的概念,满树,除最后一层外,其它层节点都用子节点。
坐标设置思路

  1. 将第一点中每次循环的tmpList保存到List<List>中。
  2. 设置最后一层节点x轴坐标,从0开始,x++
  3. 倒数第二层的节点,坐标由子节点取平均得到,以此类推,设置完所有节点坐标。 效果如下:

有点丑,实际项目是水平方向的,如下图,看着还行,没想到旋转下,就有点奇怪了,就这样吧。。。
回归正题,通过画“满树”,以及x轴坐标设置的三个步骤,是可以推测出,任意一层,比如第二层中,节点间的距离是和 以第二层节点为根节点的子树 最后一层节点个数相关,通俗的说,两个节点的子树越大,节点间的距离越大。
AB距离=(A子树底层节点个数+B子树底层节点个数)/2
进而得出x轴坐标:
Ax=Bx+(A子树底层节点个数+B子树底层节点个数)/2,另外,每层的x从0开始。(未考虑坐标放大缩小)

“满树”坐标的计算方法得到了,那么普通树呢

普通树显然是不能使用子树底层节点个数的,普通树可以使用子树的叶子节点个数。 因为,普通树可以直线加节点,变成“满树”,而满树的底层节点个数=普通树的叶子节点个数,各自的子树也同理。

结论

设置y轴,层次遍历,y++; 设置x轴,X=前一个节点的X+(前一个节点的子树个数+当前节点的子树个数)/2

延申

  1. 其实效果也并不是很好,图中,第二层两侧节点向中间移动,树会更瘦一点,比较好,影响因素只考虑了当前子树的叶子节点,需要增加旁边子树的影响,但还不知道怎么加。
  2. 重看一年前的代码,原因在于,需要实现:一棵树部分节点已知,设置其他节点 的坐标 ,要求 已知节点坐标尽量不变、需要有格式化的效果、已知节点和未知节点需要平滑过渡。

不考虑时间复杂度的实现,一点点挪;考虑效率,则差不多就行

转载于:https://juejin.im/post/5d329952f265da1b5d57e6b6

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值