二叉树的遍历大体可分为两种:广度优先(Depth-First-Search,简称DFS)和深度优先(Breadth-First-Search,简称BFS)。
其中广度优先又包含了先序遍历、中序遍历和后序遍历三种,借助栈(Stack)实现;深度优先主要指的是层次遍历,借助队列(Queue)实现。
中序遍历:左子树->根节点->右子树
顺序:7 3 8 1 9 4 0 5 2 6
后序遍历:左子树->右子树->根节点
顺序:7 8 3 9 4 1 5 6 2 0
层次遍历:从树根开始,从上到下、从左到右逐层遍历
顺序:0 1 2 3 4 5 6 7 8 9
代码使用C++,代码形式使用leetcode上的模板。代码参考自阿菜的博客。
先给出树的结构体:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
递归法很容易理解,根节点优先放进result里,然后再遍历左子树和右子树。
class Solution {
public:
vector result;
vector Traversal(TreeNode* root) {
this->findPreOrder(root);
return result;
}
void findPreOrder(TreeNode *root){
if(root != NULL){
result.push_back(root->val);
findPreOrder(root->left);
findPreOrder(root->right);
}
}
};
迭代法需要用到栈:
class Solution {
public:
vector Traversal(TreeNode* root) {
vector result;
stack TreeStack;
// 游标节点
TreeNode* node = root;
// 遍历结束时游标节点的左右子树都为空,且栈也为空
// 因此,只要不同时满足这两点,都继续循环
while(node != NULL || !TreeStack.empty()){
while(node != NULL){
// 先遍历根节点
result.push_back(node->val);
// 为了之后能找到该节点的右子树,暂存该节点
TreeStack.push(node);
// 由考察顺序可知向左走
node = node->left;
}
// 直到左子树为空,开始考虑右子树
// 当然如果栈已空,就无需再考虑
// 弹出栈顶元素,并向右走
if(!TreeStack.empty()){
node = TreeStack.top();
TreeStack.pop();
node = node->right;
}
}
return result;
}
};
同样很容易理解,不再赘述。
class Solution {
public:
vector result;
vector Traversal(TreeNode* root) {
this->findMidOrder(root);
return result;
}
void findMidOrder(TreeNode *root){
if(root != NULL){
findMidOrder(root->left);
result.push_back(root->val);
findMidOrder(root->right);
}
}
};
使用与先序遍历类似的方式:
class Solution {
public:
vector Traversal(TreeNode* root) {
vector result;
stack TreeStack;
// 游标节点
TreeNode* node = root;
while(node != NULL || !TreeStack.empty()){
while(node != NULL){
// 暂存该节点
TreeStack.push(node);
// 向左走
node = node->left;
}
// 直到左子树为空,则弹出根节点并输出
// 当然如果栈已空,就无需再考虑
// 遍历完根节点后,向右走
if(!TreeStack.empty()){
node = TreeStack.top();
TreeStack.pop();
result.push_back(node->val);
node = node->right;
}
}
return result;
}
};
同样的套路,为了完整还是写一下吧:
class Solution {
public:
vector result;
vector Traversal(TreeNode* root) {
this->findPosOrder(root);
return result;
}
void findPosOrder(TreeNode *root){
if(root != NULL){
findPosOrder(root->left);
findPosOrder(root->right);
result.push_back(root->val);
}
}
};
这里跟之前就不大一样了。因为在后序遍历中,决定是否可以输出当前节点的值的时候,需要考虑其左右子树是否都已经遍历完成。
这里设置一个lastVisit的游标,用于记录上一个输出的节点。当输出一个节点时要判断,其是否有右子树,若有、是否已经遍历。
当遍历完左子树后,和中序遍历一样,回到根节点,此时要进行一次判断,判断其右子树的情况:如果右子树为空或lastVisit为右子树,说明右子树已遍历完毕,输出该节点;否则进入右子树。
class Solution {
public:
vector Traversal(TreeNode* root) {
vector result;
stack TreeStack;
// 游标节点
TreeNode* node = root;
// lastVisit节点
TreeNode* lastVisit = root;
while(node != NULL || !TreeStack.empty()){
while(node != NULL){
// 暂存该节点
TreeStack.push(node);
// 向左走
node = node->left;
}
// 查看当前栈顶元素
node = TreeStack.top();
// 如果其右子树为空,或者右子树已经访问,则确认无误、输出
if(node.right == NULL || node.right == lastVisit){
result.push_back(node->val);
TreeStack.pop();
// 输出后修改lastVisit值为当前输出节点
lastVisit = node;
node = NULL;
}else{
//否则,继续遍历右子树
node = node.right;
}
}
return result;
}
};
这里只给出迭代法,代码改自
C++ 二叉树的层次遍历
从根节点开始,对每个节点,令其入队,同时判断其左右子节点是否为空,不为空,则依次入队。逐步输出,直到队列为空。
class Solution {
private:
vector ret;
public:
vector Traversal(TreeNode* root) {
LevelOrder(root);
return ret;
}
void LevelOrder(TreeNode *root)
{
if(root == NULL) return;
queue que;
que.push(root);
while(!que.empty()){
// 输出并弹出根节点,并判断其左右子节点是否为空,不空,则入队列
TreeNode *node = que.front();
ret.push_back(node->val);
que.pop();
if(node->left != NULL) que.push(node->left);
if(node->right != NULL) que.push(node->right);
}
}
};
与迭代法1相似,只是先入队,再输出。
/*省略……*/
void LevelOrder(TreeNode *root)
{
if(root == NULL) return;
queue que;
que.push(root);
while(!que.empty()){
if((que.front())->left != NULL) que.push((que.front())->left);
if((que.front())->right != NULL) que.push((que.front())->right);
ret.push_back((que.front())->val);
que.pop();
}
}
https://www.jianshu.com/p/456af5480cee
https://blog.csdn.net/qq_29762941/article/details/80908072
https://leetcode-cn.com/tag/tree/