解题思路:
相比于上一题 102. 二叉树的层序遍历,这道题额外要求 打印顺序交替变化。
方法一:层序遍历 + 双端队列
- 利用双端队列的两端皆可添加元素的特性,设打印列表(双端队列)
tmp
,并规定:- 奇数层 则添加至
tmp
尾部 , - 偶数层 则添加至
tmp
头部 。
- 奇数层 则添加至
算法流程:
- 特例处理: 当树的根节点为空,则直接返回空列表
[]
。 - 初始化: 打印结果空列表
res
,包含根节点的双端队列deque
。 - BFS 循环: 当
deque
为空时跳出。- 新建列表
tmp
,用于临时存储当前层打印结果。 - 当前层打印循环: 循环次数为当前层节点数(即
deque
长度)。- 出队: 队首元素出队,记为
node
。 - 打印: 若为奇数层,将
node.val
添加至tmp
尾部;否则,添加至tmp
头部。 - 添加子节点: 若
node
的左(右)子节点不为空,则加入deque
。
- 出队: 队首元素出队,记为
- 将当前层结果
tmp
转化为 list 并添加入res
。
- 新建列表
- 返回值: 返回打印结果列表
res
即可。
<,,,,,,,,,>
代码:
Python 中使用 collections 中的双端队列 deque()
,其 popleft()
方法可达到 $O(1)$ 时间复杂度;列表 list 的 pop(0)
方法时间复杂度为 $O(N)$ 。
Java 中将链表 LinkedList 作为双端队列使用。
Python
class Solution:
def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root: return []
res, deque = [], collections.deque([root])
while deque:
tmp = collections.deque()
for _ in range(len(deque)):
node = deque.popleft()
if len(res) % 2 == 0: tmp.append(node.val) # 奇数层 -> 插入队列尾部
else: tmp.appendleft(node.val) # 偶数层 -> 插入队列头部
if node.left: deque.append(node.left)
if node.right: deque.append(node.right)
res.append(list(tmp))
return res
Java
class Solution {
public List<List<Integer>> zigzagLevelOrder(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<>();
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;
}
}
复杂度分析:
- 时间复杂度 $O(N)$ : $N$ 为二叉树的节点数量,即 BFS 需循环 $N$ 次,占用 $O(N)$ ;双端队列的队首和队尾的添加和删除操作的时间复杂度均为 $O(1)$ 。
- 空间复杂度 $O(N)$ : 最差情况下,即当树为满二叉树时,最多有 $N/2$ 个树节点 同时 在
deque
中,使用 $O(N)$ 大小的额外空间。
方法二:层序遍历 + 双端队列(奇偶层逻辑分离)
- 方法一代码简短、容易实现;但需要判断每个节点的所在层奇偶性,即冗余了 $N$ 次判断。
- 通过将奇偶层逻辑拆分,可以消除冗余的判断。
算法流程:
与方法一对比,仅 BFS 循环不同。
- BFS 循环: 循环打印奇 / 偶数层,当
deque
为空时跳出。- 打印奇数层: 从左向右 打印,先左后右 加入下层节点。
- 若
deque
为空,说明向下无偶数层,则跳出。 - 打印偶数层: 从右向左 打印,先右后左 加入下层节点。
代码:
Python
class Solution:
def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root: return []
res, deque = [], collections.deque()
deque.append(root)
while deque:
tmp = []
# 打印奇数层
for _ in range(len(deque)):
# 从左向右打印
node = deque.popleft()
tmp.append(node.val)
# 先左后右加入下层节点
if node.left: deque.append(node.left)
if node.right: deque.append(node.right)
res.append(tmp)
if not deque: break # 若为空则提前跳出
# 打印偶数层
tmp = []
for _ in range(len(deque)):
# 从右向左打印
node = deque.pop()
tmp.append(node.val)
# 先右后左加入下层节点
if node.right: deque.appendleft(node.right)
if node.left: deque.appendleft(node.left)
res.append(tmp)
return res
Java
class Solution {
public List<List<Integer>> zigzagLevelOrder(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;
}
}
C++
class Solution {
public:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
deque<TreeNode*> deque;
vector<vector<int>> res;
if (root != NULL) deque.push_back(root);
while (!deque.empty()) {
// 打印奇数层
vector<int> tmp;
for(int i = deque.size(); i > 0; i--) {
// 从左向右打印
TreeNode* node = deque.front();
deque.pop_front();
tmp.push_back(node->val);
// 先左后右加入下层节点
if (node->left != NULL) deque.push_back(node->left);
if (node->right != NULL) deque.push_back(node->right);
}
res.push_back(tmp);
if (deque.empty()) break; // 若为空则提前跳出
// 打印偶数层
tmp.clear();
for(int i = deque.size(); i > 0; i--) {
// 从右向左打印
TreeNode* node = deque.back();
deque.pop_back();
tmp.push_back(node->val);
// 先右后左加入下层节点
if (node->right != NULL) deque.push_front(node->right);
if (node->left != NULL) deque.push_front(node->left);
}
res.push_back(tmp);
}
return res;
}
};
复杂度分析:
- 时间复杂度 $O(N)$ : 同方法一。
- 空间复杂度 $O(N)$ : 同方法一。
方法三:层序遍历 + 倒序
- 此方法的优点是只用列表即可,无需其他数据结构。
- 偶数层倒序: 若
res
的长度为 奇数 ,说明当前是偶数层,则对tmp
执行 倒序 操作。
<,,,,,,,,,,>
代码:
Python
class Solution:
def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root: return []
res, queue = [], collections.deque()
queue.append(root)
while queue:
tmp = []
for _ in range(len(queue)):
node = queue.popleft()
tmp.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
res.append(tmp[::-1] if len(res) % 2 else tmp)
return res
Java
class Solution {
public List<List<Integer>> zigzagLevelOrder(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;
}
}
C++
class Solution {
public:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> res;
if (root != NULL) que.push(root);
while (!que.empty()) {
vector<int> tmp;
for(int i = que.size(); i > 0; i--) {
TreeNode* node = que.front();
que.pop();
tmp.push_back(node->val);
if (node->left != NULL) que.push(node->left);
if (node->right != NULL) que.push(node->right);
}
if (res.size() % 2 == 1) reverse(tmp.begin(),tmp.end());
res.push_back(tmp);
}
return res;
}
};
复杂度分析:
- 时间复杂度 $O(N)$ : $N$ 为二叉树的节点数量,即 BFS 需循环 $N$ 次,占用 $O(N)$ 。共完成 少于 $N$ 个节点的倒序操作,占用 $O(N)$ 。
- 空间复杂度 $O(N)$ : 最差情况下,即当树为满二叉树时,最多有 $N/2$ 个树节点同时在
queue
中,使用 $O(N)$ 大小的额外空间。