【数据结构】第六章 树

树的递归定义

image

树的基本术语

image

二叉树的定义

image

image

二叉树的常用性质

image

image

二叉树的前序遍历

144. 二叉树的前序遍历
递归写法

/**
 * 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> res;
    void dfs(TreeNode* root)
    {
        if(!root) return;
        res.push_back(root->val);
        if(root->left) dfs(root->left);
        if(root->right) dfs(root->right);
    }
    vector<int> preorderTraversal(TreeNode* root) {
        dfs(root);
        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) {
        vector<int> res;
        stack<TreeNode*> stk;
        while(root || stk.size())
        {
            while(root)
            {
                res.push_back(root->val);
                stk.push(root);
                root = root->left;
            }
            root = stk.top()->right;
            stk.pop();
        }
        return res;
    }
};

迭代写法二:把while里的while换成if else

/**
 * 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) {
        vector<int> res;
        stack<TreeNode*> stk;
        while(root || stk.size())
        {
            if(root)
            {
                res.push_back(root->val);
                stk.push(root);
                root = root->left;
            }
            else
            {
                root = stk.top()->right;
                stk.pop();
            }
        }
        return res;
    }
};

二叉树的中序遍历

94. 二叉树的中序遍历
递归写法:

/**
 * 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> res;
    void dfs(TreeNode* root)
    {
        if(!root) return;
        if(root->left) dfs(root->left);
        res.push_back(root->val);
        if(root->right) dfs(root->right);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        dfs(root);
        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> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> stk;
        while(root || stk.size())
        {
            while(root)
            {
                stk.push(root);
                root = root->left;
            }
            root = stk.top();
            stk.pop();
            res.push_back(root->val);
            root = root->right;
        }
        return res;
    }
};

迭代写法二:把while内的while换成if与else

/**
 * 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*> stk;
        while(root || stk.size())
        {
            if(root)
            {
                stk.push(root);
                root = root->left;
            }
            else
            {
                root = stk.top();
                stk.pop();
                res.push_back(root->val);
                root = root->right;
            }
        }
        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> res;
    void dfs(TreeNode* root)
    {
        if(!root) return;
        if(root->left) dfs(root->left);
        if(root->right) dfs(root->right);
        res.push_back(root->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        dfs(root);
        return res;
    }
};

迭代写法一(acwing):
巧解:我们在前面已经学过了前序遍历的迭代写法。而前序遍历的遍历顺序是“根左右”,我们可以对前序遍历进行修改,使其按照“根右左”的顺序遍历,然后把得到的结果翻转一下得到的“左右根”序列就是后序遍历的结果。

/**
 * 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) {
        vector<int> res;
        stack<TreeNode*> stk;
        while(root || stk.size())
        {
            while(root)
            {
                res.push_back(root->val);
                stk.push(root);
                root = root->right; //这里是从前序遍历的root=root->left修改为right
            }
            root = stk.top()->left; // 从前序遍历的right修改为left
            stk.pop(); 
        }
        reverse(res.begin(), res.end()); //翻转得到的“根右左”序列得到“左右根”序列
        return res;
    }
};

迭代写法二(巧解):参考题解
image
在栈中加入一个空节点作为标记,当弹出空节点时说明左右子树已经遍历完了,此时访问当前节点。如果弹出的节点不空,那就按照右子树、左子树的顺序入栈(因为这样最后出来的顺序才是左右根)

/**
 * 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) {
        vector<int> res;
        stack<TreeNode*> stk;
        if (root) stk.push(root); // root非空,则入栈
        while (stk.size())
        {
            auto t = stk.top(); // 先取出栈顶元素
            stk.pop();
            // 如果非空,说明当前结点没有被访问过(访问过的节点其上面有空节点先被弹出来)
            if (t) 
            {
                stk.push(t); // 那么我们把当前节点入栈
                stk.push(NULL); // 然后再加一个空节点作为标记,表示已经访问过了
                // 先放右子树,再放左子树,这样出栈的时候就是左右根(根节点最先入栈最后出)
                if (t->right) stk.push(t->right); 
                if (t->left) stk.push(t->left);
            }
            else // 是空节点,那么说明其下一个节点就是答案数组中的元素
            {
                t = stk.top(); // 取出答案元素
                stk.pop(); // 弹栈
                res.push_back(t->val); // 将答案元素的值加入result数组
            }
        }
        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) {
        vector<int> res;
        stack<TreeNode*> stk;
        while(root || stk.size()) // while 跳过初始时x==NULL的情况
        {
            if(root->left)
            {
                stk.push(root);
                root = root->left;
            }
            else if(root->right)
            {
                stk.push(root);
                root = root->right;
            }
            else // 当前结点没有孩子
            {
                res.push_back(root->val); // 访问当前结点
                while(stk.size())
                {
                    auto t = stk.top(); // 当前栈顶结点必然是当前结点的父结点
                    if(t->left == root && t->right) // 如果当前结点是左孩子,且父结点有右孩子
                    {
                        root = t->right; // 则转向右孩子,不pop父结点
                        break;
                    }
                    else
                    {
                        stk.pop(); // 否则pop并访问该父结点(此时孩子已经全部访问完)
                        root = t; // 循环直到栈空或父结点有右孩子
                        res.push_back(root->val);
                    }
                }
            }
            if(stk.empty()) break; // 如果栈空了,说明根节点已被访问,遍历完成
        }
        return res;
    }
};

二叉树的层序遍历

102. 二叉树的层序遍历

/**
 * 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<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        queue<TreeNode*> q;
        if(root) q.push(root);

        while(q.size())
        {
            vector<int> level; // 存每层的节点
            int len = q.size(); // len为当前层节点数
            while(len -- )
            {
                auto t = q.front();
                q.pop();
                level.push_back(t->val);
                if(t->left) q.push(t->left);
                if(t->right) q.push(t->right);
            }
            res.push_back(level);
        }
        return res;
    }
};

从前序和中序遍历构造二叉树

105. 从前序与中序遍历序列构造二叉树
image
image

/**
 * 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:
    unordered_map<int, int> pos;
    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir)
    {
        if(pl > pr) return NULL;
        auto root = new TreeNode(preorder[pl]);
        int k = pos[root->val];
        root->left = build(preorder, inorder, pl + 1, pl + 1 + k - 1 - il, il, k - 1);
        root->right = build(preorder, inorder, pl + 1 + k - 1 - il + 1, pr, k + 1, ir);
        return root; 
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        for(int i = 0; i < inorder.size(); i ++ ) pos[inorder[i]] = i;
        return build(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1);
    }
};

从中序和后序遍历构造二叉树

106. 从中序与后序遍历序列构造二叉树

image

/**
 * 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:
    unordered_map<int, int> pos;
    TreeNode* build(vector<int>& inorder, vector<int>& postorder, int il, int ir, int pl, int pr)
    {
        if(pl > pr) return NULL;
        auto root = new TreeNode(postorder[pr]);
        int k = pos[root->val];
        root->left = build(inorder, postorder, il, k - 1, pl, pl + k - 1 - il);
        root->right = build(inorder, postorder, k + 1, ir, pl + k - 1 - il + 1, pr - 1);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        for(int i = 0; i < inorder.size(); i ++ ) pos[inorder[i]] = i;
        return build(inorder, postorder, 0, inorder.size() - 1, 0, postorder.size() - 1);
    }
};
posted @   Tshaxz  阅读(21)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
Language: HTML
点击右上角即可分享
微信分享提示