二叉树的先序、中序、后序、层次遍历

29 阅读3分钟

叫先根遍历、中根遍历、后根遍历,更好些,先中后是表达根的位置。

  • 先根遍历:先访问根节点,然后访问左子树, 最后访问右子树。
  • 中根遍历:先访问左子树,然后访问根节点, 最后访问右子树。
  • 后根遍历:先访问左子树,然后访问右子树, 最后访问根节点
  • 层次遍历:按照树的深度从根节点开始一层一层的访问。

先根、中根、后根遍历,有三种解法,递归方式、非递归方式,以及Morris遍历。

二叉树数据结构定义如下:

#include <vector>
#include <stack>
#include <algorithm>
#include <stdio.h>
#include <queue>

using namespace std;

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

递归实现

递归遍历实现最简单:

// 先根遍历递归写法
void preorderTraversal(TreeNode* root) {
    if (root != nullptr) {
        printf("%d\t", root->val);
        preorderTraversal(root->left);
        preorderTraversal(root->right);
    }
}

// 中根遍历递归写法
void inorderTraversal(TreeNode* root) {
    if (root != nullptr) {
        inorderTraversal(root->left);
        printf("%d\t", root->val);
        inorderTraversal(root->right);
    }
}

// 后根遍历递归写法
void postorderTraversal(TreeNode* root) {
    if (root != nullptr) {
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        printf("%d\t", root->val);
    }
}
```对于对于对于
### 非递归实现
非递归遍历实现需要用到栈,实际上递归本质也是栈,应为递归函数调用,也是将参数和变量入栈的过程。将其过程模拟出来:开始时,我们只知道树的根节点,因此从上往下进行访问。每次将根节点入栈。进入左节点时 root=root->left;进入右节点时root=root->right。到达叶子节点,则进行退栈操作,再进入另一分支, 当栈为空,root也为nullptr,遍历完毕。
##### 先根遍历和中根遍历
以前序遍历为例子,首先遍历根节点,打印根节点;然后进入递归,将根节点入栈;先序遍历是根左右,因此,先遍历左子树,root = root->left。打印root;然后依旧继续遍历左子树,直到root为空,此时开始退栈,遍历右子树,root = root->right。进入右子树后,先打印root,然后入栈,又是继续先往左递进...
中根遍历和先根遍历只有一点区别,先根遍历是先打印根;因此先打印再入栈;中根遍历时先打印左节点然后打印根,因此放到退栈时再打印。
```cpp
void preorderTraversal(TreeNode* root) {
    stack<TreeNode*> stk;
    while (!stk.empty() || root != nullptr) {
        while (root != nullptr) {
            printf("%d\t", root->val);
            stk.push(root);
            root = root->left;
        }
        root = stk.top();
        stk.pop();
        root = root->right;
    }
}
void inorderTraversal(TreeNode* root) {
    stack<TreeNode*> stk;
    while (root != nullptr || !stk.empty()) {
        while (root != nullptr) {
            stk.push(root);
            root = root->left;
        }
        root = stk.top();
        stk.pop();
        printf("%d\t",root->val);
        root = root->right;
    }
}

对于后根遍历,比较麻烦一些,先根和中根,根节点和递进节点时是连着的,而对后根来说却不是这样。 我们得运用下逆向思维:后根遍历顺序是左、右、根,我们反过来变成根、右、左,这样不就和先根遍历完全对称了么?然后将结果逆序一下,不就是得到后根遍历的答案了么?

void postorderTraversal(TreeNode *root) {
    stack<TreeNode *> stk;
    vector<int> rev;
    while (root != nullptr || !stk.empty()) {
        while (root != nullptr) {
            stk.push(root);
            rev.push_back(root->val);
            root = root->right;
        }
        root = stk.top();
        stk.pop();
        root = root->left;
    }
    for(auto it = rev.rbegin(); it != rev.end(); it++) {
        print("%d\t", *it);
    }
}

层次遍历

二叉树的层次遍历要用到队列,出队时,将左右子节点都入队。

void levelTraversal(TreeNode* root) {
    queue<TreeNode*> q;
    if(root != nullptr) {
        q.push(root);
        while(!q.empty()) {
            root = q.front();
            q.pop();
            printf("%d\t", root->val);
            if(root->left != nullptr) {
                q.push(root->left);
            }
            if(root->right != nullptr) {
                q.push(root->right);
            }
        }
    }
}