描述
题目描述
给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
示例
输入:{1,#,2}
返回值:[[1],[2]]知识点:二叉树,队列,栈,BFS
难度:⭐⭐⭐
题解
解题思路
一旦看到这种有关顺序的,第一个就要想到用栈或队列实现,有这个思路才能进一步实现、优化。
比如这道题,要求 “第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印”,显然,如果能直接利用栈或队列的特性,就能实现题目要求的各种顺序了。
方法一:双栈
图解

算法流程:
- 维护两个栈,一个存放奇数层的节点,一个存放偶数层的节点
- 整型变量level表示当前遍历第几层,奇数则顺序打印,偶数逆序打印, 从第1层开始
- 分奇数层和偶数层处理
- 奇数层按顺序,每次偶数层的栈保存偶数层节点,先存左结点,这样等下次pop时就是右节点先打印
- 偶数层反序,每次奇数层的栈保存偶数层节点,先存右结点,这样等下次pop时就是左节点先打印
Java 版本代码如下:
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if(pRoot == null) {
return res;
}
// 存放奇数层的节点
Stack<TreeNode> stack1 = new Stack<>();
stack1.push(pRoot);
// 存放偶数层的节点
Stack<TreeNode> stack2 = new Stack<>();
// 表示当前遍历第几层,奇数则顺序打印,偶数逆序打印, 从第1层开始
int level = 1;
while(!stack1.isEmpty() || !stack2.isEmpty()) {
// 处理奇数层
if(level % 2 != 0) {
ArrayList<Integer> list = new ArrayList<>();
// 奇数层按顺序
while(!stack1.isEmpty()) {
TreeNode node = stack1.pop();
if(node != null) {
// 收集打印结果
list.add(node.val);
// stack2保存偶数层节点,先存左结点,这样等下次pop时就是右节点先打印,满足题目要求
stack2.push(node.left);
stack2.push(node.right);
}
}
// 收集当前层的结果
if(!list.isEmpty()) {
res.add(list);
level++;
}
} else {
// 处理偶数层
ArrayList<Integer> list = new ArrayList<>();
while(!stack2.isEmpty()) {
TreeNode node = stack2.pop();
if(node != null) {
list.add(node.val);
// 需要按顺序push,因为stack1是用在奇数层按顺序输出结果的
stack1.add(node.right);
stack1.add(node.left);
}
}
if(!list.isEmpty()) {
res.add(list);
level++;
}
}
}
return res;
}
} Python 版本代码如下:
class Solution:
def zigzagLevelOrder(self, pRoot):
if pRoot==None:
return []
stack1=[pRoot]
stack2=[]
res1=[]
res2=[]
result=[]
while stack1 or stack2:
res1=[]
res2=[]
# 处理奇数层
while stack1:
node=stack1.pop()
# 按顺序,先存左节点,再存右节点
if node.left:
stack2.append(node.left)
if node.right:
stack2.append(node.right)
res1.append(node.val)
# 收集当前层的结果
if len(res1)!=0:
result.append(res1)
# 处理偶数层
while stack2:
node=stack2.pop()
if node.right:
stack1.append(node.right)
if node.left:
stack1.append(node.left)
res2.append(node.val)
# 收集当前层的结果
if len(res2)!=0:
result.append(res2)
return result 复杂度分析:
时间复杂度 O(N):遍历树的每个结点N
空间复杂度 O(N):题目要求返回的二维列表,不算是额外空间,但栈和List都是需要借助的额外空间
方法二:队列&双向遍历
算法流程:
- 维护一个双向队列,用于保存当前层的元素
- 每一层,队列都线加入null,表示每一层的分割点,同事用布尔值遍历表示当前方向是否是从左向右遍历的
- 进行双向遍历,直到队列中只有分隔符null
- 遇到层分隔符,表示当前已经在新的一层,接着通过迭代器和反向迭代器,进行每一层遍历结果的收集
Java 版本代码如下:
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer> > zigzagLevelOrder(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if (pRoot == null) {
return res;
}
ArrayList<Integer> list = new ArrayList<>();
LinkedList<TreeNode> queue = new LinkedList<>();
// null表示每一层的分割点
queue.offer(null);
queue.offer(pRoot);
// 当前方向是否是从左向右遍历的,第一层是从左往右的
boolean toRight = true;
// 双向遍历,直到队列中只有分隔符null
while (queue.size() != 1) {
TreeNode node = queue.poll();
// 遇到层分隔符,表示当前已经在新的一层
if (node == null) {
Iterator<TreeNode> iter = null;
// 从左往右遍历,从queue的头部元素开始迭代
if (toRight) {
iter = queue.iterator();
} else {
// 从右往左遍历,获取queue的尾部元素的迭代器
iter = queue.descendingIterator();
}
// 下次遍历的方向要反过来
toRight = !toRight;
// 遍历当前层结点,并收集当前层的结果
while (iter.hasNext()) {
TreeNode temp = (TreeNode)iter.next();
list.add(temp.val);
}
// 保存每一层的结果
res.add(new ArrayList<Integer>(list));
list.clear();
//添加层分隔符
queue.offer(null);
// 注意:例如每次遍历到这里,此时Queue队头元素还是当前层的第一个结点,上面只是poll掉分隔符null
continue;
}
// 队列加入下一层的每个结点
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
return res;
}
}复杂度分析:
时间复杂度 O(N):遍历每个结点
空间复杂度 O(N):有借助到队列实现双向遍历

京公网安备 11010502036488号