LeetCode:二叉树的遍历方式(13道经典题目)
本文带来与二叉树的遍历方法有关的经典题目,主要实现是C++。
2.1 深度优先遍历
2.1.1 递归遍历
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> vec;
traversal(root, vec);
return vec;
}
void traversal(TreeNode* root, vector<int>& vec) {
if (root == nullptr) return;
vec.push_back(root->val); // 中
traversal(root->left, vec);
traversal(root->right, vec);
}
};
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
path = []
def traversal(root):
if not root: return
path.append(root.val)
traversal(root.left)
traversal(root.right)
traversal(root)
return path
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> vec;
traversal(root, vec);
return vec;
}
void traversal(TreeNode* root, vector<int>& vec) {
if (root == nullptr) return;
traversal(root->left, vec);
traversal(root->right, vec);
vec.push_back(root->val);
}
};
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> vec;
traversal(root, vec);
return vec;
}
void traversal(TreeNode* root, vector<int>& vec) {
if (root == nullptr) return;
traversal(root->left, vec);
vec.push_back(root->val);
traversal(root->right, vec);
}
};
2.1.2 迭代法
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。所以我们可以通过迭代实现深度优先遍历。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> vec;
if (root == nullptr)return vec;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
vec.push_back(node->val);
if (node->right) st.push(node->right);
if (node->left) st.push(node->left);
}
return vec;
}
};
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
result = []
if not root: return result
st = []
st.append(root)
while st:
node = st.pop()
result.append(node.val)
if node.right:
st.append(node.right)
if node.left:
st.append(node.left)
return result
迭代法前序遍历和中序遍历后序遍历的代码不通用!
因为前序遍历的顺序是中左右,先访问的元素是中间节点,要处理的元素也是中间节点,所以刚刚才能写出相对简洁的代码,因为要访问的元素和要处理的元素顺序是一致的,都是中间节点。
那么再看看中序遍历,中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的。
那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> vec;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != nullptr || !st.empty()) {
if (cur != nullptr) { // 指针来访问节点,访问到最底层
st.push(cur); // 将访问的节点放进栈
cur = cur->left; // 左
}
else {
cur = st.top(); // 从栈里弹出的数据,就是要处理的数据
st.pop();
vec.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return vec;
}
};
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
result = []
if not root: return result
st = []
cur = root
while cur or st:
if cur:
st.append(cur)
cur = cur.left
else:
cur = st.pop()
result.append(cur.val)
cur = cur.right
return result
前序:中左右 --> 中右左 --> 左右中(reverse)
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> vec;
stack<TreeNode*> st;
if (root == nullptr) return vec;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
vec.push_back(node->val);
if (node->left) st.push(node->left);
if (node->right) st.push(node->right);
}
reverse(vec.begin(), vec.end());
return vec;
}
};
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
result = []
if not root: return result
st = []
st.append(root)
while st:
node = st.pop()
result.append(node.val)
if node.left: st.append(node.left)
if node.right: st.append(node.right)
return result[::-1]
2.1.3 二叉树的统一迭代法
注意这里是栈, 先进后出,顺序相反
以中序遍历为例,使用栈的话,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况。那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。
如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> vec;
stack<TreeNode*> st;
if (root != nullptr) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != nullptr) {
st.pop();
if (node->right) st.push(node->right);
if (node->left) st.push(node->left);
st.push(node);
st.push(nullptr);
}
else {
st.pop();
node = st.top();
st.pop();
vec.push_back(node->val);
}
}
return vec;
}
};
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root: st.append(root)
while st:
node = st.pop()
if node:
if node.right: st.append(node.right)
if node.left: st.append(node.left)
st.append(node)
st.append(None)
else:
node = st.pop()
result.append(node.val)
return result
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> vec;
stack<TreeNode*> st;
if (root != nullptr) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != nullptr) {
st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
if (node->right) st.push(node->right); // 添加右节点(空节点不入栈)
st.push(node); // 添加中节点
st.push(nullptr); // 中节点访问过,但是还没有处理,加入空节点做为标记。
if (node->left) st.push(node->left); // 添加左节点(空节点不入栈)
}
else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.top(); // 重新取出栈中元素
st.pop();
vec.push_back(node->val); // 加入到结果集
}
}
return vec;
}
};
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root: st.append(root)
while st:
node = st.pop()
if node:
if node.right: st.append(node.right)
st.append(node)
st.append(None)
if node.left: st.append(node.left)
else:
node = st.pop()
result.append(node.val)
return result
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> vec;
stack<TreeNode*> st;
if (root != nullptr) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != nullptr) {
st.pop();
st.push(node);
st.push(nullptr);
if (node->right) st.push(node->right);
if (node->left) st.push(node->left);
}
else {
st.pop();
node = st.top();
st.pop();
vec.push_back(node->val);
}
}
return vec;
}
};
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root: st.append(root)
while st:
node = st.pop()
if node:
st.append(node)
st.append(None)
if node.right: st.append(node.right)
if node.left: st.append(node.left)
else:
node = st.pop()
result.append(node.val)
return result
2.2 层次遍历
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而是用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。
迭代求法:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
int size = que.size();
vector<int> vec;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
return result;
}
};
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
result = []
if not root: return result
from collections import deque
deq = deque([root])
while deq:
res = []
n = len(deq)
for i in range(n):
node = deq.popleft()
res.append(node.val)
if node.left: deq.append(node.left)
if node.right: deq.append(node.right)
result.append(res)
return result
这是一个模板!!!以下题目都可以用这个模板求解。
递归求法:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
traversal(root, 0, result);
return result;
}
void traversal(TreeNode* root, int depth, vector<vector<int>>& vec) {
if (root == nullptr) return;
if (vec.size() == depth) vec.push_back({});
vec[depth].push_back(root->val);
if (root->left) traversal(root->left, depth + 1, vec);
if (root->right) traversal(root->right, depth + 1, vec);
}
};
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
result = []
def helper(root, depth):
if not root: return []
if len(result) == depth: result.append([])
result[depth].append(root.val)
if root.left: helper(root.left, depth + 1)
if root.right: helper(root.right, depth + 1)
helper(root, 0)
return result
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> result;
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
vector<int> vec;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
reverse(result.begin(), result.end());
return result;
}
};
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
vector<int> result;
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node->left)que.push(node->left);
if (node->right)que.push(node->right);
if (i == size - 1) result.push_back(node->val);
}
}
return result;
}
};
给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
vector<double> vec;
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
double sum = 0;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
sum += node->val;
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
vec.push_back(sum / size);
}
return vec;
}
};
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。树的序列化输入是用层序遍历,每组子节点都由 null 值分隔。
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> children;
Node(){}
Node(int val) :val(val) {}
Node(int val, vector<Node*> children) :val(val), children(children) {}
};
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
vector<vector<int>> result;
queue<Node*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
vector<int> vec;
for (int i = 0; i < size; i++) {
Node* node = que.front();
que.pop();
vec.push_back(node->val);
for (vector<Node*>::iterator it = node->children.begin(); it != node->children.end(); it++) {
if (*it != nullptr) {
que.push(*it);
}
}
}
result.push_back(vec);
}
return result;
}
};
给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
vector<int> vec;
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
int max_num = INT32_MIN;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
max_num = (max_num > node->val) ? max_num : node->val;
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
vec.push_back(max_num);
}
return vec;
}
};
给定一个 **完美二叉树 **,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
Node* nodePre;
Node* node;
for (int i = 0; i < size; i++) {
if (i == 0) {
nodePre = que.front();
que.pop();
node = nodePre;
}
else {
node = que.front();
que.pop();
nodePre->next = node;
nodePre = nodePre->next;
}
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
node->next = nullptr;
}
return root;
}
};
给定一个二叉树
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
Node* nodePre;
Node* node;
for (int i = 0; i < size; i++) {
if (i == 0) {
nodePre = que.front();
que.pop();
node = nodePre;
}
else {
node = que.front();
que.pop();
nodePre->next = node;
nodePre = nodePre->next;
}
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
nodePre->next = nullptr;
}
return root;
}
};
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。
class Solution {
public:
int maxDepth(TreeNode* root) {
int depth = 0;
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
depth++;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return depth;
}
};
给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。说明:叶子节点是指没有子节点的节点。
class Solution {
public:
int minDepth(TreeNode* root) {
int depth = 0;
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
depth++;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
if (!node->left && !node->right) return depth;
}
}
return depth;
}
};
以上题解大多来自【代码随想录】,在此基础上做了一定总结,并附带一些自己的理解。
后续题目,随缘更新,有错误请指出!
END