引言
学习堆排的过程中,总是用数组的形式来保存树型结构,输出也不是很直观。网上找过一些帖子,大家都希望能直观的打印一棵树,想到的方式也很直接,就是节点和节点之间通过斜线连接。
1
/ \
2 3
/ \ / \
4 5 6 7
但这样有个问题,如果树达到一定高度,斜线就很难准确的指向对应的数字,除非把斜线的角度和长度随着改变,但这样就涉及到画图,比较麻烦。我们完全可以把斜线去掉。
图中 * 代表每个节点,去掉斜线后,无论树有多高都不会出现变形,实现起来比较简单。
思路及实现
画一棵二叉树,我们习惯从上到下,但是如果想要通过以上方式打印一棵二叉树,我们需要从下到上,首先要确定它最底层占用了多少个格子。把输出的结果想象成一个二维数组,最底层占用的格子数决定了二维数组的X轴长度,层高决定了Y轴长度。
一棵满二叉树,所有节点按顺序存放在一维数组里,存在如下规律:
每层有多少个节点:2^(n-1)
每层最起始节点的下标:2^(n-1)-1
通过以上两个规律,我们可以计算出树的高度,顺便把节点分层保存下来,方法如下:
public static void showTree(int[] array){
int hight = 1;//层数
int len = array.length;
int p = 0;//节点下标
List<List<String>> list = new ArrayList<List<String>>();//所有节点分层保存
List<String> temp = new ArrayList<String>();//当前层节点
while ( p < len){
int start = (int) Math.pow(2,hight-1) - 1;//当前层起始节点下标
int end = start + (int) (Math.pow(2,hight-1)) - 1;//当前层结束节点下标
temp.add(p + "");
p++;
if(p > end || p >= len){ //当前层循环结束 或者 所有节点循环结束
list.add(temp);
temp = new ArrayList<String>();
if ( p < len){ //最后一个节点层数不用累加
hight++; //换层
}
}
}
}
得到树的高度,我们就能创建出效果图中的二维数组。
int lastNum = (int)Math.pow(2,hight-1);//满二叉树最后一层的节点数
int lastLen = lastNum + (lastNum - 1); //最后一层所占数组长度(每个节点之间间隔一个位置)
String[][] tree = new String[hight][lastLen];
上面Excel图中总结的规律:
每层起始节点下标:2^(hight-n)-1
每层节点下标间隔:2^(hight)
我们把之前分层保存下来的节点,按照对应下标存入二维数组即可。
for (int i = 0,size = list.size(); i < size ; i ++) {
List<String> strs = list.get(i);//当前层所有节点
int tempHight = hight - i;
int start = (int) (Math.pow(2,tempHight-1) - 1);//当前层起始节点下标
for (String str : strs) {
tree[i][start] = str;//节点
int gap = (int) Math.pow(2,tempHight);//当前层节点间的间隔
start = start + gap;//下一个节点
}
}
至此,我们就把一维数组中保存的二叉树,按照树形结构保存到二维数组,现在只需要输出即可。
//输出树
for (int i = 0 ; i < hight ; i ++){
for(int j = 0 ; j < lastLen ; j ++){
String str = tree[i][j];
System.out.format("%-2.2s",StringUtils.isEmpty(str)?"██":str);
}
System.out.println();
}