Leetcode103 二叉樹的鋸齒形層次遍歷
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
博主Github:https://github.com/GDUT-Rp/LeetCode
題目:
給定一個二叉樹,返回其節點值的鋸齒形層次遍歷。(即先從左往右,再從右往左進行下一層遍歷,以此類推,層與層之間交替進行)。
例如:
給定二叉樹 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回鋸齒形層次遍歷如下:
[
[3],
[20,9],
[15,7]
]
解題思路:
方法一:BFS(廣度優先遍歷)
直觀想法
最直觀的方法是 BFS,逐層遍歷樹。
BFS 在每層的默認順序是從左到右,因此需要調整 BFS 算法以生成鋸齒序列。
最關鍵的是使用雙端隊列遍歷,可以在隊列的任一端插入元素。
如果需要 FIFO (先進先出)的順序,則將新元素添加到隊列尾部,後插入的元素就可以排在後面。如果需要 FILO (先進後出)的順序,則將新元素添加到隊列首部,後插入的元素就可以排在前面。
算法
實現 BFS 的幾種算法。
-
使用兩層嵌套循環。外層循環迭代樹的層級,內層循環迭代每層上的節點。
-
也可以使用一層循環實現 BFS。將要訪問的節點添加到隊列中,使用 分隔符(例如:空節點)把不同層的節點分隔開。分隔符表示一層結束和新一層開始。
這裏採用第二種方法。在此算法的基礎上,藉助雙端隊列實現鋸齒形順序。在每一層,使用一個空的雙端隊列保存該層所有的節點。根據每一層的訪問順序,即從左到右或從右到左,決定從雙端隊列的哪一端插入節點。
- 實現從左到右的遍歷順序(FIFO)。將元素添加到隊列尾部,保證後添加的節點後被訪問。從上圖中可以看出,輸入序列
[1, 2, 3, 4, 5]
,按照 FIFO 順序得到輸出序列爲[1, 2, 3, 4, 5]
。 - 實現從右到左的遍歷順序(FILO)。將元素添加到隊列頭部,保證後添加的節點先被訪問。輸入序列
[1, 2, 3, 4, 5]
,按照 FILO 順序得到輸出序列爲[5, 4, 3, 2, 1]
。
C++
#include <iostream>
#include <vector>
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> result;
if (root == NULL) {
return result;
}
deque<TreeNode*> node_queue;
node_queue.push_back(root);
node_queue.push_back(NULL);
deque<int> level_list;
bool is_order_left = true;
while (node_queue.size() > 0) {
TreeNode* curr_node = node_queue.front();
node_queue.pop_front();
if (curr_node != NULL) {
if (is_order_left) {
level_list.push_back(curr_node->val);
} else {
level_list.push_front(curr_node->val);
}
if (curr_node->left != NULL) {
node_queue.push_back(curr_node->left);
}
if (curr_node->right != NULL) {
node_queue.push_back(curr_node->right);
}
} else {
vector<int> tmp;
for (int a:level_list){
tmp.push_back(a);
}
result.push_back(tmp);
level_list.clear();
if (node_queue.size() > 0) {
node_queue.push_back(NULL);
}
is_order_left = !is_order_left;
}
}
return result;
}
};
Java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
if (root == null) {
return new ArrayList<List<Integer>>();
}
List<List<Integer>> results = new ArrayList<List<Integer>>();
// add the root element with a delimiter to kick off the BFS loop
LinkedList<TreeNode> node_queue = new LinkedList<TreeNode>();
node_queue.addLast(root);
node_queue.addLast(null);
LinkedList<Integer> level_list = new LinkedList<Integer>();
boolean is_order_left = true;
while (node_queue.size() > 0) {
TreeNode curr_node = node_queue.pollFirst();
if (curr_node != null) {
if (is_order_left)
level_list.addLast(curr_node.val);
else
level_list.addFirst(curr_node.val);
if (curr_node.left != null)
node_queue.addLast(curr_node.left);
if (curr_node.right != null)
node_queue.addLast(curr_node.right);
} else {
// we finish the scan of one level
results.add(level_list);
level_list = new LinkedList<Integer>();
// prepare for the next level
if (node_queue.size() > 0)
node_queue.addLast(null);
is_order_left = !is_order_left;
}
}
return results;
}
}
Python
# -*- coding: utf-8 -*-
# @File : LeetCode103.py
# @Author : Runpeng Zhang
# @Date : 2020/3/22
# @Desc : 二叉樹的鋸齒形層次遍歷
from collections import deque
# Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def zigzagLevelOrder(self, root: TreeNode):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
ret = []
level_list = deque()
if root is None:
return []
# start with the level 0 with a delimiter
node_queue = deque([root, None])
is_order_left = True
while len(node_queue) > 0:
curr_node = node_queue.popleft()
if curr_node:
if is_order_left:
level_list.append(curr_node.val)
else:
level_list.appendleft(curr_node.val)
if curr_node.left:
node_queue.append(curr_node.left)
if curr_node.right:
node_queue.append(curr_node.right)
else:
# we finish one level
ret.append(list(level_list))
# add a delimiter to mark the level
if len(node_queue) > 0:
node_queue.append(None)
# prepare for the next level
level_list = deque()
is_order_left = not is_order_left
return ret
複雜度分析
時間複雜度:,其中 N 是樹的結點數,因爲每個結點都訪問一次。
空間複雜度:,其中 N 是樹中節點的數量。
除了輸出數組,主要的內存開銷是雙端隊列。
任何時刻,雙端隊列中最多隻存儲兩層節點。因此雙端隊列的大小不超過 ,其中 LL 是一層的最大節點數。包含最多節點的層可能是完全二叉樹的葉節點層,大約有 個節點。因此最壞情況下,空間複雜度爲 。
方法二:DFS (深度優先遍歷)
直觀想法
也可以使用 DFS 實現 BFS 的遍歷順序。
在 DFS 遍歷期間,將結果保存在按層數索引的全局數組中。即元素
array[level]
存儲同一層的所有節點。然後在 DFS 的每一步更新全局數組。
與改進的 BFS 算法類似,使用雙端隊列保存同一層的所有節點,並交替插入方向(從首部插入或從尾部插入)得到需要的輸出順序。
算法
使用遞歸實現 DFS 算法。定義一個遞歸方法 DFS(node, level)
,方法參數爲當前節點 node
和指定層數 level
。該方法共執行三個步驟:
-
如果是第一次訪問該層的節點,即該層的雙端隊列不存在。那麼創建一個雙端隊列,並添加該節點到隊列中。
-
如果當前層的雙端隊列已存在,根據順序,將當前節點插入隊列頭部或尾部。
-
最後,爲每個節點調用該遞歸方法。
C++
#include <iostream>
#include <vector>
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> results;
if (root == NULL) {
return results;
}
zigzagLevelOrderDFS_helper(root, 0, results);
return results;
}
void zigzagLevelOrderDFS_helper(TreeNode* node, int level, vector<vector<int>> & results) {
if (level >= results.size()) {
vector<int> new_level;
new_level.push_back(node->val);
results.push_back(new_level);
} else {
if (level % 2 == 0) {
results[level].push_back(node->val);
} else {
results[level].insert(results[level].begin(), node->val);
}
}
if (node->left != NULL) {
zigzagLevelOrderDFS_helper(node->left, level + 1, results);
}
if (node->right != NULL) {
zigzagLevelOrderDFS_helper(node->right, level + 1, results);
}
}
};
Java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
protected void DFS(TreeNode node, int level, List<List<Integer>> results) {
if (level >= results.size()) {
LinkedList<Integer> newLevel = new LinkedList<Integer>();
newLevel.add(node.val);
results.add(newLevel);
} else {
if (level % 2 == 0)
results.get(level).add(node.val);
else
results.get(level).add(0, node.val);
}
if (node.left != null) DFS(node.left, level + 1, results);
if (node.right != null) DFS(node.right, level + 1, results);
}
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
if (root == null) {
return new ArrayList<List<Integer>>();
}
List<List<Integer>> results = new ArrayList<List<Integer>>();
DFS(root, 0, results);
return results;
}
}
Python
# -*- coding: utf-8 -*-
# @File : LeetCode103.py
# @Author : Runpeng Zhang
# @Date : 2020/3/22
# @Desc : 二叉樹的鋸齒形層次遍歷
from collections import deque
# Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def zigzagLevelOrder(self, root: TreeNode):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
if root is None:
return []
results = []
def dfs(node, level):
if level >= len(results):
results.append(deque([node.val]))
else:
if level % 2 == 0:
results[level].append(node.val)
else:
results[level].appendleft(node.val)
for next_node in [node.left, node.right]:
if next_node is not None:
dfs(next_node, level + 1)
# normal level order traversal with DFS
dfs(root, 0)
for i, enum in enumerate(results):
results[i] = list(results[i])
return results
算法複雜度:
時間複雜度:訪問每個節點恰好一次,時間複雜度爲 ,其中 是節點的個數,也就是樹的大小。
空間複雜度:取決於樹的結構,最壞情況存儲整棵樹,因此空間複雜度是 。