二叉树、二叉搜索树

41 阅读8分钟

由前中后序遍历构造二叉树

问题分类

  1. 根据前序遍历和中序遍历构造二叉树:前序遍历的头节点一定是根节点,从前序遍历中找到根节点并在中序遍历中找到根节点对应的位置,将中序遍历数组分为两部分。
    设前序遍历数组区间为[pl, pr],中序遍历数组区间为[il, ir],设根节点在中序遍历数组中下标为t。 递归终止条件为:pl > pr 逻辑:左儿子根节点为递归前序数组[pl+1, t-il+pl],对应中序数组[il, t-1],右儿子根节点为递归前序数组[t-il+pl+1, pr-1],对应中序数组[t+1, ir]
  2. 根据中序遍历和后序遍历构造二叉树:后序遍历的尾节点一定是根节点,从后序遍历中找到根节点并在中序遍历中找到根节点对应的位置,将中序遍历数组分为两部分。
    设前序遍历数组区间为[pl, pr],中序遍历数组区间为[il, ir],设根节点在中序遍历数组中下标为t。 递归终止条件为:pl > pr 逻辑:左儿子根节点为递归后序数组[pl, t-il+pl-1],对应中序数组[il, t-1],右儿子根节点为递归后序数组[t-il+pl, pr-1],对应中序数组[t+1, ir]

leetcode.105

  • 链接leetcode.cn/problems/co…
  • 解题方法:用哈希表存中序遍历中根节点的位置
    将根节点创建出来,用上述思路解题
  • leetcode解题代码
class Solution {
public:
    unordered_map<int, int> hash;
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        for (int i = 0; i < n; i ++) hash[inorder[i]] = i;
        return build(preorder, inorder, 0, n - 1, 0, n - 1);
    }
    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir){
        if (pl > pr) return nullptr;
        auto t = hash[preorder[pl]];
        auto root = new TreeNode(preorder[pl]);
        root->left = build(preorder, inorder, pl + 1, t - il + pl, il, t - 1);
        root->right = build(preorder, inorder, t - il + pl + 1, pr, t + 1, ir);
        return root;
    }
};

leetcode.106

  • 链接leetcode.cn/problems/co…
  • 解题方法:用哈希表存中序遍历中根节点的位置
    将根节点创建出来,用上述思路解题
  • leetcode解题代码
class Solution {
public:
    unordered_map<int, int> hash;
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        for (int i = 0; i < n; i ++) hash[inorder[i]] = i;
        return build(preorder, inorder, 0, n - 1, 0, n - 1);
    }
    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir){
        if (pl > pr) return nullptr;
        auto t = hash[preorder[pl]];
        auto root = new TreeNode(preorder[pl]);
        root->left = build(preorder, inorder, pl + 1, t - il + pl, il, t - 1);
        root->right = build(preorder, inorder, t - il + pl + 1, pr, t + 1, ir);
        return root;
    }
};

构造二叉树

leetcode.654

  • 链接leetcode.cn/problems/ma…
  • 解题方法:
    在数组中找到最大的数定义为根节点,且不能改变数组中数的顺序(双指针)
    根节点的左儿子为递归根节点的左半部分
    根节点的右儿子为递归根节点的右半部分
  • leetcode解题代码
class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        int n = nums.size();
        return dfs(nums, 0, n - 1);
    }

    TreeNode* dfs(vector<int> nums, int l, int r){
        if (l > r) return nullptr;

        int pos = l;
        for (int i = l + 1; i <= r; i ++){
            if (nums[i] > nums[pos])
                pos = i;
        }
        TreeNode* root = new TreeNode(nums[pos]);
        root->left = dfs(nums, l, pos - 1);
        root->right = dfs(nums, pos + 1, r);
        return root;
    }
};

leetcode.617

  • 链接leetcode.cn/problems/me…
  • 解题方法:
    特判:如果root1或root2不存在直接返回存在的树
    逻辑:根节点相加返回新的根节点
    左儿子相加返回新的左儿子,右儿子相加返回新的右儿子
    返回值:返回新树的根节点
  • leetcode解题代码
class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (!root1) return root2;
        if (!root2) return root1;

        root1->val += root2->val;
        root1->left = mergeTrees(root1->left, root2->left);
        root1->right = mergeTrees(root1->right, root2->right);
        return root1; 
    }
};

leetcode.108

  • 链接leetcode.cn/problems/co…
  • 解题方法:
    构造二叉搜索树的根节点(升序数组的中点)
    递归中点的左右两端构造左子树和右子树
  • leetcode解题代码
class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        auto root = dfs(nums, 0, nums.size() - 1);
        return root;
    }

    TreeNode* dfs(vector<int> nums, int l, int r){
        if (l > r) return nullptr;
        // 二叉搜索树为升序排列,中点即为根节点
        int mid = (l + r) / 2;
        TreeNode* root = new TreeNode(nums[mid]);
        root->left = dfs(nums, l, mid - 1);
        root->right = dfs(nums, mid + 1, r);
        return root;
    }
};

leetcode.538

  • 链接leetcode.cn/problems/co…
  • 解题方法:
    找到比根节点大的数,即二叉搜索树的右子树
    求和并更新根节点的值
  • leetcode解题代码
class Solution {
public:
    int sum = 0;
    TreeNode* bstToGst(TreeNode* root) {
        if (!root) return nullptr;
        // 找到比根节点大的数,即二叉搜索树的右子树
        bstToGst(root->right);
        // 求和并更新根节点的值
        sum += root->val;
        root->val = sum;
        bstToGst(root->left);
        return root;
    }
};

最近公共祖先

leetcode.236

  • 链接leetcode.cn/problems/lo…
  • 解题方法:
    特判:如果根节点不存在或者根节点和p或q相等,那么根节点就是最近公共祖先
    逻辑:找到左子树的最近公共祖先,找到右子树的最近公共祖先
    如果左右子树的最近公共祖先都存在,返回根节点
    如果左子树最近公共祖先不存在,返回右子树的最近公共祖先
    如果右子树最近公共祖先不存在,返回左子树的最近公共祖先
    返回值:返回新树的根节点
  • leetcode解题代码
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root || root == p || root == q) return root;
        auto left = lowestCommonAncestor(root->left, p, q);
        auto right = lowestCommonAncestor(root->right, p, q);
        if (left && right) return root;
        if (!left) return right;
        return left;
    }
};

leetcode.235

  • 链接leetcode.cn/problems/lo…
  • 解题方法:
    二叉搜索树的性质:左儿子<根节点<右儿子
    特判:如果根节点不存在则返回根节点,由于二叉搜索树的性质左右儿子不可能等于根节点的值
    逻辑:如果p,q的值均小于根节点,说明他们的最近公共祖先在左子树,递归左子树;如果p,q的值均大于根节点,说明他们的最近公共祖先在右子树,递归右子树;如果p,q在根节点两侧他们的最近公共祖先就是根节点
  • leetcode解题代码
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root || root == p || root == q) return root;
        auto left = lowestCommonAncestor(root->left, p, q);
        auto right = lowestCommonAncestor(root->right, p, q);
        if (left && right) return root;
        if (!left) return right;
        return left;
    }
};

二叉搜索树(查找、插入、删除)

解题方法

二叉搜索树的性质

  • 若左子树不空,那左子树所有节点的值均 < 根节点的值
  • 若右子树不空,那右子树所有节点的值均 > 根节点的值
  • 左右子树也均为二叉搜索树

所以二叉搜索树的中序遍历是一个有序序列

查询

leetcode.700

  • 链接leetcode.cn/problems/se…
  • 解题方法:
    二叉搜索树的性质:左儿子<根节点<右儿子
    特判:如果根节点不存在则返回空,如果根节点的值等于val则返回根节点
    逻辑:定义答案根节点,如果根节点的值大于val则递归左子树,如果根节点的值小于val则递归右子树
  • leetcode解题代码
class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if (!root) return nullptr;
        if (root->val == val) return root;
        
        TreeNode* res = nullptr;
        if (root->val > val) res = searchBST(root->left, val);
        if (root->val < val) res = searchBST(root->right, val);
        return res;
    }
};

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if (!root) return nullptr;
        while (root){
            if (root->val == val) return root;
            else if (root->val > val) root = root->left;
            else root = root->right;
        }
        return root;
    }
};

leetcode.98

class Solution {
public:
    vector<int> res;
    bool isValidBST(TreeNode* root) {
        dfs(root, res);
        for (int i = 1; i <= res.size(); i ++){
            if (res[i] <= res[i - 1])
                return false;
        }
        return true;
    }

    void dfs(TreeNode* root, vector<int> res){
        if (!root) return;
        dfs(root->left, res);
        res.push_back(root->val);
        dfs(root->right, res);
    }
};

class Solution {
public:
    TreeNode* pre = nullptr;
    bool isValidBST(TreeNode* root) {
        stack<TreeNode*> stk;
        while (root || stk.size()){
            while (root){
                stk.push(root);
                root = root->left;
            }
            auto cur = stk.top();
            stk.pop();
            if (pre && cur->val <= pre->val) return false;
            pre = cur;
            root = cur->right;
        }
        return true;
    }
};

leetcode.530

// 递归
class Solution {
public:
    int res = INT_MAX;
    TreeNode* pre = NULL;

    int getMinimumDifference(TreeNode* root) {
        dfs(root);
        return res;
    }

    void dfs(TreeNode* cur){
        if (!cur) return;

        dfs(cur->left);
        // 如果上一个节点的值存在,则求差
        if (pre){
            res = min(res, cur->val - pre->val);
        }
        // 否则将当前节点的值赋值为上一个节点
        pre = cur;
        dfs(cur->right);
    }
};

// 迭代
class Solution {
public:
    int getMinimumDifference(TreeNode* root) {
        stack<TreeNode*> stk;
        int res = INT_MAX;
        TreeNode* pre = nullptr;
        while (root || stk.size()){
            while (root){
                stk.push(root);
                root = root->left;
            }
            auto cur = stk.top();
            stk.pop();
            if (pre)
                res = min(cur->val - pre->val, res);
            pre = cur;
            root = cur->right;
        }
        return res;
    }
};

leetcode.501

class Solution {
public:
    vector<int> res;
    int curc = 0, maxc = 0, pre;
    vector<int> findMode(TreeNode* root) {
        dfs(root);
        return res;
    }

    void dfs(TreeNode* root){
        if (!root) return;
        dfs(root->left);
        // 如果当前节点的值等于上一个节点
        if (root->val == pre){
            // 当前计数+1
            curc ++;
            // 更新上一个节点
            pre = root->val;
        }
        // 如果值不相等
        else{
            // 当前为第一个数
            curc = 1;
            // 更新上一个节点
            pre = root->val;
        }
        // 如果当前计数值大于最大个数值
        if (curc > maxc){
            // 更新最大个数
            maxc = curc;
            // 将上一个节点加入答案
            res = {pre};
        }
        // 如果出现不止一个众数,加入答案
        else if (curc == maxc) res.push_back(pre);
        dfs(root->right);
    }
};

class Solution {
public:
    vector<int> res;
    int curc = 0, maxc = 0, pre;
    vector<int> findMode(TreeNode* root) {
        stack<TreeNode*> stk;
        while (root || stk.size()){
            while (root){
                stk.push(root);
                root = root->left;
            }
            auto cur = stk.top();
            stk.pop();
            if (cur->val == pre){
                curc ++;
                pre = cur->val;
            }
            else{
                curc = 1;
                pre = cur->val;
            }
            if (curc > maxc){
                maxc = curc;
                res = {pre};
            }
            else if (curc == maxc) res.push_back(pre);
            root = cur->right;
        }
        return res;
    }
};

插入

leetcode.701

  • 链接leetcode.cn/problems/in…
  • 解题方法:查找插入的值所在的位置,如果头节点不存在则返回新头节点,如果val大于根节点,递归右子树找插入的位置,如果val小于根节点,递归左子树找插入的位置
  • leetcode解题代码
class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (!root) return new TreeNode(val);

        if (root->val > val) root->left = insertIntoBST(root->left, val);
        else root->right = insertIntoBST(root->right, val);
        return root;
    }
};

删除

leetcode.450

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (!root) return nullptr;
        if (root->val > key) root->left = deleteNode(root->left, key);
        else if (root->val < key) root->right = deleteNode(root->right, key);
        else {
            // key为叶子节点
            if (!root->left && !root->right){
                root = nullptr;
                return root;
            }
            // key只有右子树
            else if (!root->left && root->right) return root->right;
            // key只有左子树
            else if (root->left && !root->right) return root->left;
            // key左右子树都有
            // 找到右子树中左下角的儿子,将当前节点的左儿子接到右子树左下角的儿子上
            else {
                TreeNode* cur = root->right;
                while (cur->left){
                    cur = cur->left;
                }
                cur->left = root->left;
                return root->right;
            }
        }
        return root;
    }
};

修改

leetcode.669

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (!root) return NULL;
        // 如果根节点不再[low, high]的范围内,递归找到根节点
        if (root->val < low) return trimBST(root->right, low, high);
        if (root->val > high) return trimBST(root->left, low, high);
        // 找到根节点,递归找到左右子树
        root->left = trimBST(root->left, low, high);
        root->right = trimBST(root->right, low, high);
        return root;
    }
};