【算法与数据结构】144、94、145、LeetCode二叉树的前中后遍历(递归法、迭代法)

所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解

一、题目

在这里插入图片描述
在这里插入图片描述

二、递归算法

  思路分析:这道题比较简单,不多说了,大家直接看代码就行。注意前中后遍历是指中间节点的遍历顺序。同时中序和后序的代码也很类似,这里给出三道题代码。前中后递归遍历算法对比下来,仅仅改变了一两行代码,就改变了结点的遍历顺序
  前序遍历程序如下

class Solution {
public:
    // 前序遍历
    void traversal_preOrder(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        vec.push_back(cur->val);                // 中
        traversal_preOrder(cur->left, vec);     // 左
        traversal_preOrder(cur->right, vec);    // 右
    }

    vector<int> preorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        vector<int> result;
        traversal_preOrder(root, result);
        return result;
    }
};

复杂度分析可以参考这篇文章二叉树多种遍历的时间复杂度和空间复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

  中序遍历程序如下

// 中序遍历
class Solution2 {
public:  
    void traversal_midOrder(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        traversal_midOrder(cur->left, vec);     // 左
        vec.push_back(cur->val);                // 中
        traversal_midOrder(cur->right, vec);    // 右
    }

    vector<int> inorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        vector<int> result;
        traversal_midOrder(root, result);
        return result;
    }
};

  后序遍历程序如下

// 后序遍历
class Solution3 {
public:
    void traversal_postOrder(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        traversal_postOrder(cur->left, vec);     // 左
        traversal_postOrder(cur->right, vec);    // 右
        vec.push_back(cur->val);                 // 中
    }

    vector<int> postorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        vector<int> result;
        traversal_postOrder(root, result);
        return result;
    }
};

三、迭代算法

3.1 迭代算法1

  遍历树节点的时候要注意是右节点先入栈,左节点后入栈,这样在出栈的时候就是左节点先出栈。
  前序遍历程序如下

// 前序遍历迭代法 中左右
class Solution4 {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        stack<TreeNode*> st;
        vector<int> result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->right) st.push(node->right);    // 空节点不入栈
            if (node->left) st.push(node->left);      // 空节点不入栈,左节点后入栈,先出栈           
        }
        return result;
    }
};

  中序遍历是左中右,需要一层层找到最左边的节点,在遍历的过程当中依次将遍历元素压入栈,最左边元素就在栈顶,可以首先输出,就实现左中右顺序的遍历。
  中序遍历程序如下

// 中序遍历迭代法 左中右
class Solution5 {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        stack<TreeNode*> st;
        vector<int> result;
        TreeNode* node = root;
        while (node != NULL || !st.empty()) {   // 栈非空或节点非空, 每次循环更新node
            if (node != NULL) {
                st.push(node);
                node = node->left;
            }
            else {
                node = st.top();
                st.pop();
                result.push_back(node->val);
                node = node->right;    // 右节点
            }    
        }
        return result;
    }
};

  后序遍历只需要将前序遍历翻转一下就能实现。
  后序遍历程序如下

// 后序遍历迭代法 左右中
class Solution6 {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->left) st.push(node->left);    // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right);  // 空节点不入栈
        }
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }
};

3.2 迭代算法2 ——统一风格写法

  在迭代算法1中,迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。因此这里我们选择用一种统一的风格来写前中后遍历的迭代算法。目的就是实现类似递归算法中改变一两行代码就能前中后遍历相互转换
  迭代算法1中中序遍历使用栈实现,它无法解决访问节点和处理节点不一致的情况,也就是说中序遍历时。我们是先访问中节点,然后在访问右节点和左节点,但是处理时,是先处理左节点、然后才是中节点和右节点。为了解决这个问题,我们将要访问的节点放入栈中,把要处理的节点也放入栈中,同时做标记,表示已经入栈,但是还没有处理。代码如下,可以看出,前中后遍历都只改动了一两行代码。
  中序遍历程序如下

// 中序遍历,统一代码风格迭代写法
class Solution7 {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if (root != NULL) st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                if (node->right) st.push(node->right);  // 添加右节点(空节点不入栈)
                st.push(node);                          // 添加中节点
                st.push(NULL);                          // 添加标记空指针,表示中结点访问过,但还没处理
                if (node->left) st.push(node->left);    // 添加左节点(空节点不入栈)
            }
            else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();                       // 将空节点弹出
                node = st.top();                // 重新取出栈中元素
                st.pop();
                result.push_back(node->val);    // 加入到结果集
            }
        }
        return result;
    }
};

  前序遍历程序如下

// 前序遍历,统一代码风格迭代写法
class Solution8 {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if (root != NULL) st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop();
                if (node->right) st.push(node->right);  // 右
                if (node->left) st.push(node->left);    // 左
                st.push(node);                          // 中
                st.push(NULL);
            }
            else {
                st.pop();
                node = st.top();
                st.pop();
                result.push_back(node->val);
            }
        }
        return result;
    }
};

  后序遍历程序如下

// 后序遍历,统一风格迭代写法
class Solution9 {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if (root != NULL) st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop();
                st.push(node);                          // 中
                st.push(NULL);
                if (node->right) st.push(node->right);  // 右
                if (node->left) st.push(node->left);    // 左
            }
            else {
                st.pop();
                node = st.top();
                st.pop();
                result.push_back(node->val);
            }
        }
        return result;
    }
};

四、完整代码

# include <iostream>
# include <vector>
# include <stack>
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) {}
};
// 前序遍历递归法
class Solution {
public:   
    void traversal_preOrder(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        vec.push_back(cur->val);                // 中
        traversal_preOrder(cur->left, vec);     // 左
        traversal_preOrder(cur->right, vec);    // 右
    }

    vector<int> preorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        vector<int> result;
        traversal_preOrder(root, result);
        return result;
    }
};
// 中序遍历递归法
class Solution2 {
public:  
    void traversal_midOrder(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        traversal_midOrder(cur->left, vec);     // 左
        vec.push_back(cur->val);                // 中
        traversal_midOrder(cur->right, vec);    // 右
    }

    vector<int> inorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        vector<int> result;
        traversal_midOrder(root, result);
        return result;
    }
};
// 后序遍历递归法
class Solution3 {
public:
    void traversal_postOrder(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        traversal_postOrder(cur->left, vec);     // 左
        traversal_postOrder(cur->right, vec);    // 右
        vec.push_back(cur->val);                // 中
    }

    vector<int> postorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        vector<int> result;
        traversal_postOrder(root, result);
        return result;
    }
};
// 前序遍历迭代法 中左右
class Solution4 {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        stack<TreeNode*> st;
        vector<int> result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->right) st.push(node->right);    // 空节点不入栈,右节点
            if (node->left) st.push(node->left);    // 空节点不入栈,左节点后入栈,先出栈           
        }
        return result;
    }
};
// 中序遍历迭代法 左中右
class Solution5 {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        if (root == NULL) return {};
        if (root->left == NULL && root->right == NULL) return { root->val };
        stack<TreeNode*> st;
        vector<int> result;
        TreeNode* node = root;
        while (node != NULL || !st.empty()) {   // 栈非空或节点非空, 每次循环更新node
            if (node != NULL) {
                st.push(node);
                node = node->left;
            }
            else {
                node = st.top();
                st.pop();
                result.push_back(node->val);
                node = node->right;    // 右节点
            }    
        }
        return result;
    }
};
// 后序遍历迭代法 左右中
class Solution6 {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->left) st.push(node->left);    // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right);  // 空节点不入栈
        }
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }
};
// 中序遍历,统一代码风格迭代写法
class Solution7 {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if (root != NULL) st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                if (node->right) st.push(node->right);  // 添加右节点(空节点不入栈)
                st.push(node);                          // 添加中节点
                st.push(NULL);                          // 添加标记空指针,表示中结点访问过,但还没处理
                if (node->left) st.push(node->left);    // 添加左节点(空节点不入栈)
            }
            else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();                       // 将空节点弹出
                node = st.top();                // 重新取出栈中元素
                st.pop();
                result.push_back(node->val);    // 加入到结果集
            }
        }
        return result;
    }
};
// 前序遍历,统一代码风格迭代写法
class Solution8 {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if (root != NULL) st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop();
                if (node->right) st.push(node->right);  // 右
                if (node->left) st.push(node->left);    // 左
                st.push(node);                          // 中
                st.push(NULL);
            }
            else {
                st.pop();
                node = st.top();
                st.pop();
                result.push_back(node->val);
            }
        }
        return result;
    }
};
// 后序遍历,统一风格迭代写法
class Solution9 {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if (root != NULL) st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop();
                st.push(node);                          // 中
                st.push(NULL);
                if (node->right) st.push(node->right);  // 右
                if (node->left) st.push(node->left);    // 左
            }
            else {
                st.pop();
                node = st.top();
                st.pop();
                result.push_back(node->val);
            }
        }
        return result;
    }
};

void my_print(vector <int>& v, string msg)
{
    cout << msg << endl;
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << "  ";
    }
    cout << endl;
}

int main() {
    // 构造树
    TreeNode* node3 = new TreeNode(3);
    TreeNode* node2 = new TreeNode(2, node3, NULL);
    TreeNode* root = new TreeNode(1, NULL, node2);
    Solution7 s1;
    vector<int> result = s1.inorderTraversal(root);
    my_print(result, "中序遍历结果:");
	system("pause");
	return 0;
}

end

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晚安66

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值