二叉树的遍历方式
理解并熟练运用二叉树的遍历方式,对理解二叉树相关题目非常重要,尤其是使用递归解决二叉树的题目思路。很多二叉树的题目其最基础的地方都是在与遍历方式的选择。
理解二叉树的递归遍历,是理解回溯的基础。
二叉树的三种基本遍历方式
根据中间节点被访问的顺序,把二叉树的遍历分为前序、中序、后序三种,这三种遍历方法又称深度优先遍历 (DFS)
前序遍历
中左右
中序遍历
左中右
二叉搜索树的中序遍历结果是升序序列
后序遍历
左右中
后序遍历的递归实现其实就是一种回溯的利用
递归实现
递归遍历代码非常简洁,只需要稍微改变一下代码顺序就能实现三种遍历方式。
前序遍历
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
vec.push_back(cur->val); // 中
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
中序遍历
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec); // 左
vec.push_back(cur->val); // 中
traversal(cur->right, vec); // 右
}
后序遍历
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
vec.push_back(cur->val); // 中
}
迭代实现
递归的本质就是通过栈的调用来实现的,借助栈数据结构我们可以通过迭代来遍历
前序遍历
前序遍历节点在出栈的同时访问,实现起来非常简单
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sta;
if (root != nullptr) sta.push(root);
while (!sta.empty()) {
TreeNode* cur = sta.top();
sta.pop();
res.push_back(cur->val); //中
if (cur->right) sta.push(cur->right); //右节点先入栈后访问
if (cur->left) sta.push(cur->left); //左节点后入栈先访问
}
return res;
}
};
中序遍历
中序遍历实现起来要复杂一些,因为中序遍历栈的操作和节点的访问操作不是同时的,所以需要一个额外的指针来控制节点的遍历
主要区别在于:
- 循环条件变化:由于增加了一个指针,循环的条件变成栈不为空或者指针不为空,只有两者都为空时才说明全部遍历完了
- 额外指针作用:指针优先将左节点入栈,直到左节点为空,这时候通过栈操作来访问节点,然后指针指向出栈节点的右节点,重复以上操作。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sta;
TreeNode* cur = root; //添加一个用来遍历的额外指针
while (!sta.empty() || cur != NULL) {
if (cur != NULL) { //遍历的节点不为空就加入栈中
sta.push(cur);
cur = cur->left; //左
} else { //cur 为空,sta不为空
TreeNode* tmp = sta.top();
sta.pop();
res.push_back(tmp->val); //中
cur = tmp->right; //右
}
}
return res;
}
};
后序遍历
-
方法一:在先序遍历的基础上更改,后序遍历是左右中,先序是中左右
中左右 ---> 中右左 ---> 左右中
经过两次变化
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sta;
if (root == nullptr) return res;
sta.push(root);
while (!sta.empty()) {
TreeNode* node = sta.top();
sta.pop();
res.push_back(node->val);
if (node->left) sta.push(node->left); //变化一:节点入栈顺序翻转
if (node->right) sta.push(node->right);
}
reverse(res.begin(), res.end()); //变化二:最终结果翻转
return res;
}
};
-
方法二:方法一比较投机取巧同时非常好理解,方法二用了和中序遍历迭代法相似的思路——增加两个指针
在返回根节点时,需要知道是从右节点返回的,还是从左节点返回点,所以还需要一个辅助指针指向上一个访问的节点
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sta;
TreeNode* cur = root; //遍历指针
TreeNode* pre = nullptr; //辅助指针,指向上一次访问过的节点
while (cur || !sta.empty()) {
if (cur) {
sta.push(cur);
cur = cur->left; //左
} else {
cur = sta.top();
if(cur->right && cur->right!=pre) { //当右节点还没被访问时
cur = cur->right; //右
} else { //右节点被访问过了
res.push_back(cur->val); //中
sta.pop();
pre = cur; //更新辅助指针
cur = nullptr;
}
}
}
return res;
}
};
层次遍历(BFS)
前面实现二叉树的前、中、后序遍历时都是使用了栈的数据结构,以二叉树的深度为优先进行遍历。
在实现层次遍历,也就是二叉树的广度优先遍历时(BFS),需要使用队列。
下面时 BFS 的通用模板
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> que;
if (root != nullptr) {
que.push(root);
}
while(!que.empty()) {
//记录每层输出的个数
int size = que.size();
vector<int> row; //row 记录每层输出结果
while(size--) {
TreeNode* tmp = que.front();
que.pop();
if(tmp->left) que.push(tmp->left);
if(tmp->right) que.push(tmp->right);
row.push_back(tmp->val);
}
res.push_back(row);
}
return res;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具