题目地址:
https://www.lintcode.com/problem/binary-tree-vertical-order-traversal/description
给定一棵二叉树,要求垂直对其进行遍历,想象树根的坐标是 ( 0 , 0 ) (0,0) (0,0),对于每个节点 ( x , y ) (x,y) (x,y),其左右孩子的坐标分别是 ( x + 1 , y − 1 ) (x+1,y-1) (x+1,y−1)和 ( x + 1 , y + 1 ) (x+1,y+1) (x+1,y+1)。遍历的结果应当是若干列表形成的一个大的列表,这些列表按 y y y的次序是从小到大。每个列表内部的数字应该按其在树中的深度由小到大排序。如果两个节点的坐标相等,则应该左边的在前右边的在后。
思路是DFS。开一个列表,存储每一列的节点,同时DFS的时候要将坐标作为参数不断传递下去,同时还需要记录一下 y = 0 y=0 y=0这个列对应的列表的下标是多少,这样才能知道一个节点应该加到哪个列表里面去。DFS的顺序按照先左子树后右子树来。但是还有一个问题,就是这样DFS之后,每个列表内部的节点不一定是按照深度排序的,所以还需要另外开一个类,存每个节点的深度,最后汇总答案的时候把每一列按照深度再排个序即可。代码如下:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Solution {
// zeroIdx记录y = 0那一列对应的list的下标。
// zeroIdx是实时更新的,所以要设为全局变量同步修改
private int zeroIdx;
class Pair {
int val, depth;
public Pair(int val, int depth) {
this.val = val;
this.depth = depth;
}
}
/**
* @param root: the root of tree
* @return: the vertical order traversal
*/
public List<List<Integer>> verticalOrder(TreeNode root) {
// write your code here
List<List<Integer>> res = new ArrayList<>();
List<List<Pair>> lists = new ArrayList<>();
dfs(root, 0, 0, lists);
// 对lists里每个list按照深度排序。
// 由于java里对于对象的排序是稳定的排序,所以不用担心左右的顺序会颠倒
for (List<Pair> list : lists) {
list.sort((p1, p2) -> Integer.compare(p1.depth, p2.depth));
List<Integer> col = new ArrayList<>();
for (Pair pair : list) {
col.add(pair.val);
}
res.add(col);
}
return res;
}
private void dfs(TreeNode cur, int x, int y, List<List<Pair>> lists) {
if (cur == null) {
return;
}
if (zeroIdx + y < 0) {
lists.add(0, new ArrayList<>());
zeroIdx++;
} else if (zeroIdx + y >= lists.size()) {
lists.add(new ArrayList<>());
}
lists.get(zeroIdx + y).add(new Pair(cur.val, x));
dfs(cur.left, x + 1, y - 1, lists);
dfs(cur.right, x + 1, y + 1, lists);
}
}
class TreeNode {
int val;
TreeNode left, right;
public TreeNode(int val) {
this.val = val;
}
}
时间复杂度
O
(
n
+
w
h
log
h
+
w
2
)
O(n+wh\log h+w^2)
O(n+whlogh+w2),
w
w
w是树的宽度,
w
2
w^2
w2的意思是,在lists.add(0, new ArrayList<>())
这一步,是需要将所有list后移的。如果这里用链表,虽然首尾插入时间
O
(
1
)
O(1)
O(1),但是索引时间变大了,并不会减少复杂度。空间
O
(
n
)
O(n)
O(n)。
注解:
这里的时间复杂度还有优化的空间。我们可以两次DFS,第一次DFS的时候先不断向左走,把
y
y
y等于负的那部分list先new出来加进res,然后第二次DFS的时候再去执行上面代码中的逻辑。这样时间复杂度可以降到
O
(
n
+
w
h
log
h
)
O(n+wh\log h)
O(n+whlogh)。当然在宽度不够宽的时候,改进其实有限。