题目描述:
给你一棵二叉树的根节点 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 。
Level | AC rate |
Medium | 68.3% |
题目给出树结点类的定义如下:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
题目解析:
我先说一下我看到这道题的一个想法,首先自然而然地会想到,先用一次二叉树遍历,找到树的最大深度然后确定链表的一个大小,根据题目所给的公式再进行一次遍历填值。还是由于题做少了的原因,我第一反应是这样可能会引起运行超时,然后就一直在思考怎样通过一次遍历来得到最后的答案,通过观察,我发现结果特别像层序遍历,所以我用了一次层序遍历,把每一层的元素按从左到右的顺序放到一个链表中,再将这个链表作为list中的一个元素,也就是一层。然后再根据遍历最后得到的深度大小,来进行数据填充,按一定的规律将“”填入链表中,考虑到层序遍历得到的结果会忽略掉结点的空间位置,即我只知道结点在哪一层,以及某一层结点的顺序,而不知道结点在哪一列从而导致出现了问题,即使我考虑了当结点为null时,我往该位置填""并返回但还是会出现问题,比如说这样一个二叉树:
根据层序遍历返回的值应该为:[[1],[2,3],[4],[5]],即使我们在null值的结点填入"",返回的值仍然是:[[1],[2,3],[4,"","",""],["",5]],需要对最后一层进行填充,需要根据上一层元素的情况了判断是进行前填充还是后填充,然后需要根据矩阵的大小对中间元素""进行填充过后将剩余的尺寸差距除2分别填在前面和后面,就没有继续再做下去了,但是本着代码不能够白写的原因,代码如下:
class Solution {
List<List<String>> list;
public List<List<String>> printTree(TreeNode root) {
list = new ArrayList<List<String>>();
CXBL(root,1);
list.remove(list.size()-1);
int col = (int)Math.pow(2,list.size())-1;
if(list.size()==1)return list;
for(int i = 1;i<list.size();i++){
int cz = (int)Math.pow(2,(list.size()-i))-1;
while(cz!=0){
list.get(i).add((int)Math.pow(2,i-1),"");
cz--;
}
}
for(int i = 0;i<list.size();i++){
int cz = (col-list.get(i).size())/2;
while(cz!=0){
list.get(i).add(0,"");
list.get(i).add("");
cz--;
}
}
return list;
}
void CXBL(TreeNode root, int depth){
int len = list.size();
List<String> temp;
if(depth>len) temp = new ArrayList<String>();
else temp = list.get(depth-1);
if(root==null){
temp.add("");
if(depth>len)list.add(temp);
else list.set(depth-1,temp);
return;
}
else temp.add(String.valueOf(root.val));
if(depth>len)list.add(temp);
else list.set(depth-1,temp);
CXBL(root.left,depth+1);
CXBL(root.right,depth+1);
}
}
没有接着往下完善的原因是因为这个做法肯定是比两次遍历的时间复杂度和空间复杂度都高的,这个代码可以过两个示例,但到后面的题会有一些问题。
然后是两次遍历的做法,就像一开头所说的,第一次遍历我们找出树最大的深度根据换算得到我们链表的一个大小,将该大小的链表内的所有值都赋为"",最后再通过一次遍历,根据题目所给出的公式可以算到一个结点的左结点和右结点在该链表中的具体位置,将对应的值填进去,最后返回链表即可,代码如下:
class Solution {
int H;
List<List<String>> list;
public List<List<String>> printTree(TreeNode root) {
list = new ArrayList<List<String>>();
H = 0;
search_h(root,1);
int col = (int)Math.pow(2,H)-1;
for(int i = 0;i<H;i++){
List<String> temp = new ArrayList<String>();
for(int j = 0;j<col;j++){
temp.add("");
}
list.add(temp);
}
fill(root,1,(col-1)/2);
// System.out.println(col);
return list;
}
void search_h(TreeNode root, int depth){
if(root==null)return;
H = Math.max(H,depth);
search_h(root.left,depth+1);
search_h(root.right,depth+1);
}
void fill(TreeNode root, int r, int c){
list.get(r-1).set(c,String.valueOf(root.val));
if(root.left!=null)fill(root.left,r+1,c-(int)Math.pow(2,H-r-1));
if(root.right!=null)fill(root.right,r+1,c+(int)Math.pow(2,H-r-1));
}
}
执行用时:1 ms, 在所有 Java 提交中击败了90.51%的用户
内存消耗:41.9 MB, 在所有 Java 提交中击败了26.90%的用户