程式碼隨想錄演算法訓練營day14 | leetcode 144. 二叉樹的前序遍歷、145. 二叉樹的後序遍歷、94. 二叉樹的中序遍歷

Humphreyr發表於2024-03-06

目錄
  • 題目連結:144. 二叉樹的前序遍歷-簡單
  • 題目連結:145. 二叉樹的後序遍歷-簡單
  • 題目連結:94. 二叉樹的中序遍歷-簡單

遞迴三要素:

  1. 確定遞迴函式的引數和返回值: 確定哪些引數是遞迴的過程中需要處理的,那麼就在遞迴函式里加上這個引數, 並且還要明確每次遞迴的返回值是什麼進而確定遞迴函式的返回型別。
  2. 確定終止條件: 寫完了遞迴演算法, 執行的時候,經常會遇到棧溢位的錯誤,就是沒寫終止條件或者終止條件寫的不對,作業系統也是用一個棧的結構來儲存每一層遞迴的資訊,如果遞迴沒有終止,作業系統的記憶體棧必然就會溢位。
  3. 確定單層遞迴的邏輯: 確定每一層遞迴需要處理的資訊。在這裡也就會重複呼叫自己來實現遞迴的過程。

題目連結:144. 二叉樹的前序遍歷-簡單

題目描述:

給你二叉樹的根節點 root ,返回它節點值的 前序 遍歷。

方法一:遞迴法

程式碼如下:

/**
 * Definition for a binary tree node.
 * 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 pre(TreeNode* cur, vector<int>& res){
        if(cur == NULL)
            return;
        res.push_back(cur->val);
        pre(cur->left, res);
        pre(cur->right, res);
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        pre(root, res);
        return res;
    }
};

方法二:迭代法

遞迴的實現就是:每一次遞迴呼叫都會把函式的區域性變數、引數值和返回地址等壓入呼叫棧中

前序遍歷是中左右,每次先處理的是中間節點,那麼先將根節點放入棧中,然後將右孩子加入棧,再加入左孩子。

為什麼要先加入 右孩子,再加入左孩子呢? 因為這樣出棧的時候才是中左右的順序。

在前序和後序遍歷中,訪問的元素和要處理的元素順序是一致的,都是中間節點。

程式碼如下:

/**
 * Definition for a binary tree node.
 * 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:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> res;
        if (root == NULL) return res;
        st.push(root);
        while (!st.empty()) {
            TreeNode* cur = st.top(); // 中
            st.pop();
            res.push_back(cur->val);
            if (cur->right)			// 右
                st.push(cur->right);
            if (cur->left)			// 左
                st.push(cur->left);
        }
        return res;
    }
};

題目連結:145. 二叉樹的後序遍歷-簡單

題目描述:

給你一棵二叉樹的根節點 root ,返回其節點值的 後序遍歷

方法一:遞迴法

程式碼如下:

/**
 * Definition for a binary tree node.
 * 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 post(TreeNode* cur, vector<int>& res){
        if(cur == NULL)
            return;
        post(cur->left, res);
        post(cur->right, res);
        res.push_back(cur->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        post(root, res);
        return res;
    }
};

方法二:迭代法

前序到後序

程式碼如下:

/**
 * Definition for a binary tree node.
 * 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:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> res;
        if (root == NULL) return res;
        st.push(root);
        while (!st.empty()) {
            TreeNode* cur = st.top(); // 中
            st.pop();
            res.push_back(cur->val);
            if (cur->left)			// 左
                st.push(cur->left);
            if (cur->right)			// 右
                st.push(cur->right);
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

題目連結:94. 二叉樹的中序遍歷-簡單

題目描述:

給定一個二叉樹的根節點 root ,返回 它的 中序 遍歷

程式碼如下:

方法一:遞迴法

/**
 * Definition for a binary tree node.
 * 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 in(TreeNode* cur, vector<int>& res){
        if(cur == NULL)
            return;
        in(cur->left, res);
        res.push_back(cur->val);
        in(cur->right, res);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        in(root, res);
        return res;
    }
};

方法二:迭代法

那麼再看看中序遍歷,中序遍歷是左中右,先訪問的是二叉樹頂部的節點,然後一層一層向下訪問,直到到達樹左面的最底部,再開始處理節點(也就是在把節點的數值放進result陣列中),這就造成了處理順序和訪問順序是不一致的。

那麼在使用迭代法寫中序遍歷,就需要借用指標的遍歷來幫助訪問節點,棧則用來處理節點上的元素。

二叉樹中序遍歷(迭代法)

程式碼如下:

/**
 * Definition for a binary tree node.
 * 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:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st;
        TreeNode* cur = root; // 根是否為空也會在while迴圈中做判斷
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) {   // 指標來訪問節點,訪問到最底層
                st.push(cur);    // 將訪問的節點放進棧
                cur = cur->left; // 左
            } else {
                cur = st.top(); // 從棧裡彈出的資料,就是要處理的資料(放進result陣列裡的資料)
                st.pop();
                res.push_back(cur->val); // 中
                cur = cur->right;        // 右,右若為空則在接下來的迴圈中判斷,若不為空處理這棵子樹
            }
        }
        return res;
    }
};

相關文章