携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情
题目描述
给你一棵二叉树的根节点 root ,请你构造一个下标从 0 开始、大小为 m x n 的字符串矩阵 res ,用以表示树的 格式化布局 。构造此格式化布局矩阵需要遵循以下规则:
树的 高度 为 height ,矩阵的行数 m 应该等于 height + 1 。 矩阵的列数 n 应该等于 2height+1 - 1 。 根节点 需要放置在 顶行 的 正中间 ,对应位置为 res[0][(n-1)/2] 。 对于放置在矩阵中的每个节点,设对应位置为 res[r][c] ,将其左子节点放置在 res[r+1][c-2height-r-1] ,右子节点放置在 res[r+1][c+2height-r-1] 。 继续这一过程,直到树中的所有节点都妥善放置。 任意空单元格都应该包含空字符串 "" 。 返回构造得到的矩阵 res 。
示例 1:
输入:root = [1,2] 输出: [["","1",""], ["2","",""]] 示例 2:
输入:root = [1,2,3,null,4] 输出: [["","","","1","","",""], ["","2","","","","3",""], ["","","4","","","",""]]
提示:
- 树中节点数在范围 [1, 210] 内
- -99 <= Node.val <= 99
- 树的深度在范围 [1, 10] 内
解题思路
矩阵的行代表每一行节点的值,因为矩阵的列数n=2height+1 - 1,一定是个奇数。根节点存在与顶行的正中间,即res[0][(n-1)/2]。
剩下的叶子节点位置如下排布:
假设父节点位置为行位置为a,列为之为b,则它的叶子节点在矩阵中的行位置为a+b,列位置为 左叶子节点=b-2height-a-1;右叶子节点=b+2height-a-1;以此类推得到矩阵res。
因为所有位置都和height有关系,所以最开始需要求出height的值。
可以采用广度优先的方法:
先设置一个height值,初始化为0,将当前层的所有节点放入一个数组中,然后判断数组内容是否为空,为空则直接返回height;
如果不为空,则遍历这个数组,将数组中节点的做右叶子节点放入新数组中,再将其赋值到到要遍历的数组中,然后累加height。
接下来就是获取m和n。
因为整个矩阵中只有根节点比较特殊,根节点元素位于第一行中间的位置,其它节点都是按照父节点的左右按照公式分布的,所以这里可以用一个递归来设置矩阵数据,递归的终止条件为二叉树所有节点为空;
递归中如果还有不为空的节点,则继续调用递归链路设置res矩阵。
代码实现
public List<List<String>> printTree(TreeNode root) {
List<List<String>> res = new ArrayList<>();
if (root == null) {
return res;
}
// 求出height
int height = getHeight(root);
int m = height + 1;
int n = (int) Math.pow(2,height+1) - 1;
int[][] arr = new int[m][n];
for (int i = 0;i < m;i ++) {
for (int j = 0;j < n;j ++) {
arr[i][j] = -100;
}
}
// 确定首行的位置
int a = 0;
int b = (n-1)/2;
arr[a][b] = root.val;
// 根据上一行的位置,获取下一行的位置
setLeafNode(arr,a,b,height,root);
getListByArr(res,arr);
return res;
}
private void getListByArr(List<List<String>> res, int[][] arr) {
for (int[] ints : arr) {
List<String> strings = new ArrayList<>();
for (int anInt : ints) {
if (anInt == -100) {
strings.add("");
} else {
strings.add(String.valueOf(anInt));
}
}
res.add(strings);
}
}
/**
* 根据父节点位置设置叶子节点位置
* @param arr 需要设置的二维数组
* @param a 父节点行位置
* @param b 父节点列位置
* @param height 总层高
* @param nodes 二叉树
*/
private void setLeafNode(int[][] arr, int a, int b, int height,TreeNode nodes) {
// 如果节点中的元素为空则直接返回
if (nodes == null) {
return;
}
// 行位置
int x = a + 1;
List<TreeNode> newNodes = new ArrayList<>();
// 右叶子节点
if (nodes.right != null) {
newNodes.add(nodes.right);
int y = b + (int) Math.pow(2,height-a-1);
// b+2height-a-1
arr[x][y] = nodes.right.val;
setLeafNode(arr,x,y,height,nodes.right);
}
// 左叶子节点
if (nodes.left != null) {
newNodes.add(nodes.left);
int y = b - (int) Math.pow(2,height-a-1);
// b+2height-a-1
arr[x][b - (int) Math.pow(2,height-a-1)] = nodes.left.val;
setLeafNode(arr,x,y,height,nodes.left);
}
}
/**
* 获取height
*/
private int getHeight(TreeNode root) {
if (root == null) {
return 0;
}
List<TreeNode> nodes = new ArrayList<>();
nodes.add(root);
int height = 0;
while (!nodes.isEmpty()) {
List<TreeNode> newNodes = new ArrayList<>();
for (TreeNode node : nodes) {
if (node.right != null) {
newNodes.add(node.right);
}
if (node.left != null) {
newNodes.add(node.left);
}
}
if (!newNodes.isEmpty()) {
height++;
}
nodes = newNodes;
}
return height;
}