五月集训(第19天)—二叉树

二叉树

1. 144. 二叉树的前序遍历

    思路1: 递归实现
        先序遍历,当前节点-->左子节点-->右子节点

/**
 * 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 {
    vector<int> ret;

    void dfs(TreeNode *root) {
        if (root) {
            ret.push_back(root->val);
            dfs(root->left);
            dfs(root->right);
        }
    }
public:
    vector<int> preorderTraversal(TreeNode* root) {
        ret.clear();
        dfs(root);
        return ret;
    }
};

递归还能写的再优雅些

class Solution {
    vector<int> ret;
public:
    vector<int> preorderTraversal(TreeNode* root) {
        if (root == nullptr) return {};
        ret.push_back(root->val);
        preorderTraversal(root->left); 
        preorderTraversal(root->right);
        return ret;
    }
};

    思路2: 迭代实现
        先序遍历,当前节点-->左子节点-->右子节点
        利用栈模拟递归过程,注意现将右子节点压栈,再将左子节点压栈,才能保证遍历树时,先访问左子节点,后访问右子节点。
        在遍历每个节点时直接访问(ret.push_back(node->val)

class Solution {
public:
    // 迭代实现
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode *> stk;
        vector<int> ret;
        ret.clear();

        if (root) stk.push(root);
        TreeNode *node = root;
        while (!stk.empty()) {
            node = stk.top();   /* 取出栈顶 */
            stk.pop();
            ret.push_back(node->val);    /* 访问栈顶节点,即当前子树根节点 */
            // 先将右子节点压栈,再将左子节点压栈,保证访问的顺序是,先左子节点,再访问右子节点
            if (node->right) stk.push(node->right);
            if (node->left) stk.push(node->left);
        }
        return ret;
    }
};

2. 94. 二叉树的中序遍历

    思路1: 递归实现
        中序遍历,左子节点-->当前节点-->右子节点

class Solution {
    vector<int> ret;

    void dfs(TreeNode *root) {
        if (root) {
            dfs(root->left);
            ret.push_back(root->val);
            dfs(root->right);
        }
    }
public:
    vector<int> inorderTraversal(TreeNode* root) {
        ret.clear();
        dfs(root);
        return ret;
    }
};

递归还能写的再优雅些

class Solution {
    vector<int> ret;
public:
    vector<int> inorderTraversal(TreeNode* root) {
        if (root == nullptr) return {};
        inorderTraversal(root->left);
        ret.push_back(root->val);
        inorderTraversal(root->right);
        return ret;
    }
};

    思路2: 迭代实现
        中序遍历,左子节点-->当前节点-->右子节点
        利用栈模拟递归过程,先向左遍历到最左节点,有右子节点,则向右子节点遍历,否则返回上一层向右遍历,直到遍历结束。
        在遍历完左子树后,访问栈顶节点,即先访问左子节点,再访问根节点(ret.push_back(node->val)

class Solution {
public:
    // 迭代实现
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode *> stk;
        vector<int> ret;
        ret.clear();

        TreeNode *node = root;
        while (!stk.empty() || node) {  /* 栈非空或者当前节点不为空指针 */
        // 栈空,当前节点不为空:   可遍历当前节点的右子树
        // 栈非空,当前节点空:     返回到上一层遍历其右子树
        // 栈非空,当前节点非空:   继续遍历当前节点右子树
        // 栈空,当前节点空:       所有节点都被遍历过了,遍历结束
            
            while (node) {  /* 找到最左子节点 */
                stk.push(node);
                node = node->left;
            }
            
            node = stk.top();
            ret.push_back(node->val);
            stk.pop();  /* 返回上一层 */
            node = node->right; /* 遍历右子树 */
        }
        return ret;
    }
};

3. 145. 二叉树的后序遍历

    思路1: 递归实现
        后序遍历,左子节点-->右子节点-->当前节点

class Solution {
    vector<int> ret;
    void dfs(TreeNode *root) {
        if (root) {
            dfs(root->left);
            dfs(root->right);
            ret.push_back(root->val);
        }
    }
public:
    vector<int> postorderTraversal(TreeNode* root) {
        ret.clear();
        dfs(root);
        return ret;
    }
};

递归还能写的再优雅些

class Solution {
    vector<int> ret;
public:
    vector<int> postorderTraversal(TreeNode* root) {
        if (root == nullptr) return {};
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        ret.push_back(root->val);
        return ret;
    }
};

    思路2: 迭代实现
        后序遍历,左子节点-->右子节点-->当前节点
        麻烦一些,先记录上次弹栈的节点,即被处理过的子树的根节点,用当前节点的左右子节点与其比较,判断左右子节点是否被遍历过。
        左子节点:如果不为空,且左右子树未被遍历过则入栈,访问左子树
        右子节点:如果不为空,且右子树未被遍历过则入栈,访问右子树
        如果左右子树都被遍历过,则当前节点弹栈,相当于访问完左右子树后访问当前子树根节点。

class Solution {
    vector<int> ret;
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode *> stk;
        vector<int> ret;
        ret.clear();

        if (root != nullptr) stk.push(root);
        TreeNode *last_pop = root;
        TreeNode *node = root;
        while (!stk.empty()) {
            node = stk.top();
            if (node->left != nullptr && node->left != last_pop && node->right != last_pop) {   /* 先访问左子节点,遍历左子树 */
            // 要遍历左子树,则要左子树没有被遍历过,即上次遍历完的节点既不是node->left,也不是node->right(因为先访问左子节点,如果右子节点被访问过了,则非空左子节点一定被访问过了)
                stk.push(node->left);
            }
            else if (node->right != nullptr && node->right != last_pop) {   /* 再访问右子节点,遍历右子树 */
            // 遍历右子树,则要求右子树没有遍历过,即上次遍历完的节点不是node->right
                stk.push(node->right);
            } else {    /* 最后访问当前子树根节点 */
                ret.push_back(node->val);
                stk.pop();
                last_pop = node;
            }
        }
        return ret;
    }
};

迭代还能写的再优雅些

    思路3: 迭代实现
        后序遍历,左子节点-->右子节点-->当前节点
        修改前序遍历结果:中左右 --> 中右左,将结果反向输出(利用栈的性质,压入,弹出)就得到了后续遍历结果左右中
        具体实现的解释在代码注释中

class Solution {
    vector<int> ret;
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode *> stk;
        stack<TreeNode *> ans_stk;
        vector<int> ret;
        ret.clear();

        if (root) stk.push(root);
        TreeNode *node = root;
        while (!stk.empty()) {  /* 修改先序遍历压栈顺序,导致遍历顺序修改: 中左右 --> 中右左 */
            node = stk.top();
            stk.pop();
            ans_stk.push(node); /* 将以 中右左 为顺序的遍历结果压入ans_stk中 */
            if (node->left) stk.push(node->left);
            if (node->right) stk.push(node->right);
        }

        while (!ans_stk.empty()) {  /* 将遍历结果从栈中弹出,遍历顺序变为 左右中 即为后序遍历 */
            ret.push_back(ans_stk.top()->val);
            ans_stk.pop();
        }

        return ret;
    }
};

4. 104. 二叉树的最大深度

    思路1:
        记录每个节点的深度,利用先序遍历,更新深度最大值。

/**
 * 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 {
    int max_depth = 0;

    void dfs(TreeNode *root, int depth) {
        if (root) {
            dfs(root->left, depth + 1);
            max_depth = max(max_depth, depth);
            dfs(root->right, depth + 1);
        }
    }
public:
    int maxDepth(TreeNode* root) {
        dfs(root, 1);
        return max_depth;
    }
};

    思路2:
        我对递归的理解还有很大的提升空间,直接利用当前函数递归求取其左右子树的深度。返回其左右子树最大深度 + 1。

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        return max( maxDepth(root->left), maxDepth(root->right) ) + 1;
    }
};

posted @ 2022-05-19 07:54  番茄元  阅读(20)  评论(0)    收藏  举报