Medium | LeetCode 103 | 剑指 Offer 32 - III. 从上到下打印二叉树 III | 二叉树层次遍历
剑指 Offer 32 - III. 从上到下打印二叉树 III
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
例如:
给定二叉树: [3,9,20,null,null,15,7]
,
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[20,9],
[15,7]
]
方法一: 双栈
由于在遍历时, 当前层的遍历顺序与下一层的遍历顺序相反, 即本层的先放到栈的孩子在遍历下一层时后遍历, 后放到栈的孩子在遍历下一层时先遍历。所以应当使用栈的数据结构。
使用两个栈实现, 一个栈用于遍历本层的节点, 另一个栈用于存放下一层的节点。
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
Deque<TreeNode> stack1 = new ArrayDeque<>();
Deque<TreeNode> stack2 = new ArrayDeque<>();
stack1.addLast(root);
while (!stack1.isEmpty()) {
List<Integer> res1 = new ArrayList<>();
while(!stack1.isEmpty()) {
TreeNode node = stack1.pop();
res1.add(node.val);
// 本层是奇数层, 遍历的顺序是从左到右, 所以添加孩子的顺序是先添加左孩子, 再添加右孩子
if (node.left != null) {
stack2.push(node.left);
}
if (node.right != null) {
stack2.push(node.right);
}
}
res.add(res1);
// 接着遍历偶数层
List<Integer> res2 = new ArrayList<>();
while(!stack2.isEmpty()) {
TreeNode node = stack2.pop();
res2.add(node.val);
// 偶数层遍历的顺序是从右到左, 所以应当先添加右孩子, 再添加左孩子
if (node.right != null) {
stack1.push(node.right);
}
if (node.left != null) {
stack1.push(node.left);
}
}
if (res2.size() > 0) {
res.add(res2);
}
}
return res;
}
方法二: 双端队列
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()) {
LinkedList<Integer> tmp = new LinkedList<>();
// 入队到queue的过程还是和普通的层次队列一样
for(int i = queue.size(); i > 0; i--) {
TreeNode node = queue.poll();
// 不过出队添加到结果集的过程, 添加到结果集的头部还是尾部就不同了
if(res.size() % 2 == 0) tmp.addLast(node.val); // 偶数层 -> 队列头部
else tmp.addFirst(node.val); // 奇数层 -> 队列尾部
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
res.add(tmp);
}
return res;
}
方法三:奇偶层逻辑分离
和方法一类似, 不过这里并不需要使用两个队列或者栈, 这里使用一个队列, 不过遍历的顺序是不同的, 和方法一类似, 奇数层的时候, 从从头部出队, 偶数层的时候是从尾部出队, 所以其添加孩子的顺序也不相同。
public List<List<Integer>> levelOrder(TreeNode root) {
Deque<TreeNode> deque = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) deque.add(root);
while(!deque.isEmpty()) {
// 打印奇数层
List<Integer> tmp = new ArrayList<>();
for(int i = deque.size(); i > 0; i--) {
// 从左向右打印
TreeNode node = deque.removeFirst();
tmp.add(node.val);
// 先左后右加入下层节点
if(node.left != null) deque.addLast(node.left);
if(node.right != null) deque.addLast(node.right);
}
res.add(tmp);
if(deque.isEmpty()) break; // 若为空则提前跳出
// 打印偶数层
tmp = new ArrayList<>();
for(int i = deque.size(); i > 0; i--) {
// 从右向左打印
TreeNode node = deque.removeLast();
tmp.add(node.val);
// 先右后左加入下层节点
if(node.right != null) deque.addFirst(node.right);
if(node.left != null) deque.addFirst(node.left);
}
res.add(tmp);
}
return res;
}
方法四: 层次遍历 + 倒序
这个方法和方法二类似。层次遍历还是常规的层次遍历的方法, 不过在添加到结果集的时候, 可以根据其层数, 选择是否需要将结果颠倒。
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()) {
List<Integer> tmp = new ArrayList<>();
for(int i = queue.size(); i > 0; i--) {
TreeNode node = queue.poll();
tmp.add(node.val);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
if(res.size() % 2 == 1) Collections.reverse(tmp);
res.add(tmp);
}
return res;
}