细谈二叉树前序遍历,中序遍历,后序遍历的迭代与递归实现

二叉树的三种遍历方式

二叉树的三种遍历方式,不同之处在于对根节点访问顺序的先后所导致的。

  • 前序遍历: 根 左 右
  • 中序遍历: 左 根 右
  • 后序遍历: 左 右 根

下面对三种遍历分别进行递归与非递归实现。重点对迭代法进行讲解。

前序遍历

在这里插入图片描述

递归

递归法较为简单, 通过先访问根节点,然后将左右节点分别当作根节点重新访问,结束条件是节点为空。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ret;
        _help(root, ret);
        return ret;
    }
    void _help(TreeNode* root, vector<int>& ret) {
        if (root == nullptr) {
            return;
        }
        ret.push_back(root->val);
        _help(root->left, ret);
        _help(root->right, ret);
    }
};

迭代

迭代进行二叉树的前序遍历,就是模仿编译器里面的栈帧对函数的调用。
思路:

  1. 建立一个栈用来存储节点,然后我们对而二叉树的节点进行访问,并且存储进栈,访问完毕之后继续向该节点的左节点进行访问,一直循环访问,直到该节点没有左节点为止。
  2. 此时 我们的栈顶元素存储的就是最后一个左节点,我们跳出循环对该节点的右节点访问,并且将该节点pop出栈,然后依然像 1. 中一样访问。
  3. 当栈为空并且当前访问的节点也为空时,说明我们访问完了这棵树。

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ret;
        stack<TreeNode*> s1;
        TreeNode* cur = root;
        while (cur || !s1.empty()) { //循环结束条件 
            while (cur) { //循环访问左节点
                ret.push_back(cur->val);
                s1.push(cur);
                cur = cur->left;
            }
            TreeNode* top = s1.top(); //当前路径左节点访问完了, 更改路径继续访问左节点
            s1.pop();
            cur = top->right;
        }
        return ret;
    }
};

中序遍历

递归

递归和前序遍历类似,只是访问节点的于进入递归的顺序有一点不同。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
     vector<int> ret;
     _help(root, ret);
     return ret;   
    }
    void _help(TreeNode* root, vector<int>& ret) {
        if (root == nullptr) {
            return;
        }
        _help(root->left, ret);
        ret.push_back(root->val);
        _help(root->right, ret);
    }
};

迭代

迭代法进行中序遍历的思路和前序遍历比较类似,
思路:

  1. 建立栈存储节点, 循环对将左节点入栈,直到左节点为空时,对栈顶元素(最后一个左节点进行访问)。并出栈。
  2. 判断栈顶节点是否有右孩子,(无左孩子,无需判断) 有右孩子,将右孩子当作根节点,重复 1. 过程。
  3. 无右孩子进入下次循环(重复1. 2. 过程)。
  4. 直到栈为空并且当前节点为为空时结束。

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        stack<TreeNode*> s1;
        TreeNode* cur = root;
        while (cur || !s1.empty()) {//循环终止条件
            while (cur) {//找到最左节点,并将该路径上的所有节点都入栈
                s1.push(cur);
                cur = cur->left;
            }
            TreeNode* top = s1.top();//栈顶元素为最左节点,对其进行访问
            ret.push_back(top->val);
            s1.pop();//访问完毕需要出栈
            if (top->right != nullptr) { //当该节点的右节点不为空时,对右节点进行循环访问
                cur = top->right;
            }
        }
        return ret;
    }
};

后序遍历

递归

访问顺序左右根

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        _help(root, ret);
        return ret;
    }
    void _help(TreeNode* root, vector<int>& ret) {
        if (root == nullptr) {
            return;
        }
        _help(root->left, ret);
        _help(root->right, ret);
        ret.push_back(root->val);
    }
};

迭代法

思路:

  1. 建立栈存储节点,将节点的左孩子不为空,将节点依次入栈,更新节点位置到左节点,直到左孩子为空,结束循环。
  2. 保存栈顶元素, 如果该节点的右孩子为空,说明可以对该节点进行访问,(因为该节点的左右孩子都是空)或者该节点的右孩子不为空 但是等于 上一轮循环的栈顶节点,也可以对该节点进行访问(因为说明该节点已经经过遍历了)。
  3. 不满足 2. 就进入右孩子重复 1 2 过程。

在这里插入图片描述

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        TreeNode* cur = root;
        stack<TreeNode*> s1;
        TreeNode* lastNode = nullptr;//用来存储上一次循环的栈顶元素

        while(cur || !s1.empty()) {//结束条件
            while(cur) {
                s1.push(cur);
                cur = cur->left;
            }

            TreeNode* top = s1.top();//最左边没访问的节点
            
            if (top->right == nullptr || lastNode == top->right) {//判断是否可以访问
                ret.push_back(top->val);
                 lastNode = top;
                s1.pop();
            }else {
                cur = top->right;
            }
        }
        return ret;
    }
};

猜你喜欢

转载自blog.csdn.net/ifwecande/article/details/106855132