树的遍历
以Leetcode上面的题目为例演示二叉树、N叉树的递归遍历以及经典非递归遍历方式。
一. 二叉树的遍历
二叉树的定义
- C++
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
- Java
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x){
val = x;
}
}
Leetcode 0144 二叉树的前序遍历
问题描述
-
给你二叉树的根节点
root
,返回它节点值的 前序 遍历。
分析
- 前序遍历:根左右
代码
- C++
// 递归
/**
* 执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
* 内存消耗:8.2 MB, 在所有 C++ 提交中击败了73.18%的用户
*/
class Solution {
public:
vector<int> res;
vector<int> preorderTraversal(TreeNode *root) {
dfs(root);
return res;
}
void dfs(TreeNode *root) {
if (!root) return;
res.push_back(root->val);
dfs(root->left);
dfs(root->right);
}
};
// 非递归
/**
* 执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
* 内存消耗:8.2 MB, 在所有 C++ 提交中击败了65.63%的用户
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode *root) {
vector<int> res;
stack<TreeNode *> stk;
while (root || stk.size()) {
while (root) {
res.push_back(root->val);
stk.push(root);
root = root->left;
}
root = stk.top()->right;
stk.pop();
}
return res;
}
};
- Java
/**
* 递归算法
* 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
* 内存消耗:36.6 MB, 在所有 Java 提交中击败了82.16%的用户
*/
public class Solution {
ArrayList<Integer> res = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
dfs(root);
return res;
}
private void dfs(TreeNode root) {
if (root == null) return;
res.add(root.val);
dfs(root.left);
dfs(root.right);
}
}
/**
* 非递归算法
* 执行用时:1 ms, 在所有 Java 提交中击败了45.75%的用户
* 内存消耗:36.8 MB, 在所有 Java 提交中击败了30.16%的用户
*/
public class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
Deque<TreeNode> stk = new ArrayDeque<>();
while (root != null || !stk.isEmpty()) {
while (root != null) {
res.add(root.val);
stk.push(root);
root = root.left;
}
root = stk.pop().right;
}
return res;
}
}
Leetcode 0094 二叉树的中序遍历
问题描述
-
给你二叉树的根节点
root
,返回它节点值的 中序 遍历。
分析
- 中序遍历:左根右
代码
- C++
// 递归
/**
* 执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
* 内存消耗:8.2 MB, 在所有 C++ 提交中击败了60.90%的用户
*/
class Solution {
public:
vector<int> res;
vector<int> inorderTraversal(TreeNode *root) {
dfs(root);
return res;
}
void dfs(TreeNode *root) {
if (!root) return;
dfs(root->left);
res.push_back(root->val);
dfs(root->right);
}
};
// 非递归
/**
* 执行用时:4 ms, 在所有 C++ 提交中击败了47.82%的用户
* 内存消耗:8.1 MB, 在所有 C++ 提交中击败了94.94%的用户
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode *root) {
vector<int> res;
stack<TreeNode *> stk;
while (root || stk.size()) {
while (root) {
stk.push(root);
root = root->left;
}
root = stk.top(), stk.pop();
res.push_back(root->val);
root = root->right;
}
return res;
}
};
- Java
/**
* 递归算法
* 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
* 内存消耗:36.6 MB, 在所有 Java 提交中击败了70.81%的用户
*/
public class Solution {
ArrayList<Integer> res = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
dfs(root);
return res;
}
private void dfs(TreeNode node) {
if (node == null) return;
dfs(node.left);
res.add(node.val);
dfs(node.right);
}
}
/**
* 非递归算法
* 执行用时:1 ms, 在所有 Java 提交中击败了41.71%的用户
* 内存消耗:36.9 MB, 在所有 Java 提交中击败了12.74%的用户
*/
public class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
Deque<TreeNode> stk = new ArrayDeque<>();
while (root != null || !stk.isEmpty()) {
while (root != null) {
stk.push(root);
root = root.left;
}
root = stk.pop();
res.add(root.val);
root = root.right;
}
return res;
}
}
Leetcode 0145 二叉树的后序遍历
问题描述
-
给你二叉树的根节点
root
,返回它节点值的 后序 遍历。
分析
- 后序遍历:左右根
代码
- C++
// 递归
/**
* 执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
* 内存消耗:8.1 MB, 在所有 C++ 提交中击败了87.63%的用户
*/
class Solution {
public:
vector<int> res;
vector<int> postorderTraversal(TreeNode *root) {
dfs(root);
return res;
}
void dfs(TreeNode *root) {
if (!root) return;
dfs(root->left);
dfs(root->right);
res.push_back(root->val);
}
};
// 非递归
/**
* 执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
* 内存消耗:8.1 MB, 在所有 C++ 提交中击败了94.56%的用户
*/
class Solution {
public:
// 思路:后序遍历是:左右根,反过来就是:根右左; 按照根右左遍历后,结果再翻转一下即可
vector<int> postorderTraversal(TreeNode *root) {
vector<int> res;
stack<TreeNode *> stk;
while (root || stk.size()) {
while (root) {
res.push_back(root->val);
stk.push(root);
root = root->right;
}
root = stk.top()->left;
stk.pop();
}
reverse(res.begin(), res.end());
return res;
}
};
- Java
/**
* 递归算法
* 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
* 内存消耗:37 MB, 在所有 Java 提交中击败了5.32%的用户
*/
public class Solution {
ArrayList<Integer> res = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
dfs(root);
return res;
}
private void dfs(TreeNode node) {
if (node == null) return;
dfs(node.left);
dfs(node.right);
res.add(node.val);
}
}
/**
* 非递归算法
* 执行用时:1 ms, 在所有 Java 提交中击败了53.31%的用户
* 内存消耗:36.7 MB, 在所有 Java 提交中击败了54.95%的用户
*/
public class Solution {
// 思路:后序遍历是:左右根,反过来就是:根右左; 按照根右左遍历后,结果再翻转一下即可
public List<Integer> postorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
Deque<TreeNode> stk = new ArrayDeque<>();
while (null != root || !stk.isEmpty()) {
while (null != root) {
res.add(root.val);
stk.push(root);
root = root.right;
}
root = stk.pop().left;
}
Collections.reverse(res);
return res;
}
}
Leetcode 0102 二叉树的层序遍历
问题描述
-
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
分析
- 层序遍历:逐层地,从左到右访问所有节点
代码
- C++
/**
* 执行用时:4 ms, 在所有 C++ 提交中击败了89.06%的用户
* 内存消耗:11.3 MB, 在所有 C++ 提交中击败了97.27%的用户
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode *root) {
vector<vector<int>> res;
queue<TreeNode *> q;
if (root) q.push(root);
while (q.size()) {
vector<int> level;
int len = q.size();
while (len--) {
auto t = q.front();
q.pop();
level.push_back(t->val);
if (t->left) q.push(t->left);
if (t->right) q.push(t->right);
}
res.push_back(level);
}
return res;
}
};
- Java
/**
* 执行用时:1 ms, 在所有 Java 提交中击败了94.42%的用户
* 内存消耗:38.8 MB, 在所有 Java 提交中击败了21.83%的用户
*/
public class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
Queue<TreeNode> q = new LinkedList<>();
if (root != null) q.add(root);
while (!q.isEmpty()) {
List<Integer> level = new ArrayList<>();
int len = q.size();
for (int i = 0; i < len; i++) {
TreeNode t = q.remove();
level.add(t.val);
if (t.left != null) q.add(t.left);
if (t.right != null) q.add(t.right);
}
res.add(level);
}
return res;
}
}
二. N叉树的遍历
N叉树的定义
- C++
class Node {
public:
int val;
vector<Node *> children;
Node() {}
Node(int _val) { val = _val; }
Node(int _val, vector<Node *> _children) {
val = _val;
children = _children;
}
};
- Java
public class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val) { val = _val; }
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
Leetcode 0589 N叉树的前序遍历
问题描述
分析
- N叉树的前序遍历:根,从左到右遍历该根对应的孩子
代码
- C++
// 递归
/**
* 执行用时:16 ms, 在所有 C++ 提交中击败了96.71%的用户
* 内存消耗:11.3 MB, 在所有 C++ 提交中击败了62.70%的用户
*/
class Solution {
public:
vector<int> res;
vector<int> preorder(Node *root) {
dfs(root);
return res;
}
void dfs(Node *root) {
if (!root) return;
res.push_back(root->val);
for (auto c : root->children)
dfs(c);
}
};
// 非递归
/**
* 执行用时:28 ms, 在所有 C++ 提交中击败了64.12%的用户
* 内存消耗:11.2 MB, 在所有 C++ 提交中击败了72.09%的用户
*/
class Solution {
public:
vector<int> preorder(Node *root) {
vector<int> res;
stack<Node *> stk;
if (root) stk.push(root);
while (stk.size()) {
auto t = stk.top(); stk.pop();
res.push_back(t->val);
reverse(t->children.begin(), t->children.end());
for (auto c : t->children) {
if (c) stk.push(c);
}
}
return res;
}
};
- Java
/**
* 递归
* 执行用时:1 ms, 在所有 Java 提交中击败了95.48%的用户
* 内存消耗:39.6 MB, 在所有 Java 提交中击败了56.39%的用户
*/
public class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> preorder(Node root) {
dfs(root);
return res;
}
private void dfs(Node root) {
if (root == null) return;
res.add(root.val);
if (root.children != null)
for (Node child : root.children)
dfs(child);
}
}
/**
* 非递归
* 执行用时:5 ms, 在所有 Java 提交中击败了16.09%的用户
* 内存消耗:39.7 MB, 在所有 Java 提交中击败了38.13%的用户
*/
public class Solution {
public List<Integer> preorder(Node root) {
List<Integer> res = new ArrayList<>();
Deque<Node> stk = new ArrayDeque<>();
if (root != null) stk.push(root);
while (!stk.isEmpty()) {
Node t = stk.pop();
res.add(t.val);
if (t.children != null) {
Collections.reverse(t.children);
for (Node child : t.children)
if (child != null)
stk.push(child);
}
}
return res;
}
}
Leetcode 0590 N叉树的后序遍历
问题描述
分析
-
N叉树的前序遍历:从左到右遍历该根对应的孩子,然后遍历根
-
思路:类似于二叉树的后续遍历,我们可以按如下方式解决:
(1) 按照
根 子节点n ... 子节点2 子节点1
方式遍历(2) 翻转得到结果
代码
- C++
// 递归
/**
* 执行用时:20 ms, 在所有 C++ 提交中击败了87.55%的用户
* 内存消耗:11.3 MB, 在所有 C++ 提交中击败了61.37%的用户
*/
class Solution {
public:
vector<int> res;
vector<int> postorder(Node *root) {
dfs(root);
return res;
}
void dfs(Node *root) {
if (!root) return;
for (auto c : root->children)
dfs(c);
res.push_back(root->val);
}
};
// 非递归
/**
* 执行用时:24 ms, 在所有 C++ 提交中击败了72.41%的用户
* 内存消耗:11.2 MB, 在所有 C++ 提交中击败了79.58%的用户
*/
class Solution {
public:
vector<int> postorder(Node *root) {
vector<int> res;
stack<Node *> stk;
if (root) stk.push(root);
while (stk.size()) {
auto t = stk.top(); stk.pop();
res.push_back(t->val);
for (auto c : t->children) {
if (c) stk.push(c);
}
}
reverse(res.begin(), res.end());
return res;
}
};
- Java
/**
* 递归
* 执行用时:1 ms, 在所有 Java 提交中击败了96.32%的用户
* 内存消耗:39.7 MB, 在所有 Java 提交中击败了56.65%的用户
*/
public class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> postorder(Node root) {
dfs(root);
return res;
}
private void dfs(Node root) {
if (root == null) return;
if (root.children != null)
for (Node child : root.children)
dfs(child);
res.add(root.val);
}
}
/**
* 非递归
* 执行用时:4 ms, 在所有 Java 提交中击败了40.11%的用户
* 内存消耗:39.4 MB, 在所有 Java 提交中击败了47.68%的用户
*/
public class Solution {
public List<Integer> postorder(Node root) {
List<Integer> res = new ArrayList<>();
Deque<Node> stk = new ArrayDeque<>();
if (root != null) stk.push(root);
while (!stk.isEmpty()) {
Node node = stk.pop();
res.add(node.val);
if (node.children != null)
for (Node child : node.children)
if (child != null)
stk.push(child);
}
Collections.reverse(res);
return res;
}
}
Leetcode 0429 N叉树的层序遍历
问题描述
分析
- N叉树的层序遍历:逐层地,从左到右访问所有节点
代码
- C++
/**
* 执行用时:20 ms, 在所有 C++ 提交中击败了84.18%的用户
* 内存消耗:11.4 MB, 在所有 C++ 提交中击败了97.76%的用户
*/
class Solution {
public:
vector<vector<int>> levelOrder(Node *root) {
vector<vector<int>> res;
if (!root) return res;
queue<Node *> q;
q.push(root);
while (q.size()) {
int len = q.size();
vector<int> line;
while (len--) {
auto t = q.front(); q.pop();
line.push_back(t->val);
for (auto c : t->children)
q.push(c);
}
res.push_back(line);
}
return res;
}
};
- Java
/**
* Created by WXX on 2021/2/28 18:04
* 执行用时:4 ms, 在所有 Java 提交中击败了45.55%的用户
* 内存消耗:39.3 MB, 在所有 Java 提交中击败了70.71%的用户
*/
public class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Queue<Node> q = new LinkedList<>();
q.add(root);
while (!q.isEmpty()) {
int len = q.size();
List<Integer> line = new ArrayList<>();
while (len-- != 0) {
Node t = q.remove();
line.add(t.val);
if (t.children != null)
for (Node c : t.children)
q.add(c);
}
res.add(line);
}
return res;
}
}