每日算法刷题Day76:10.19:leetcode 二叉树12道题,用时3h

十、创建二叉树

1.套路

1.大多数题给定数组构造二叉树 ,本质上是通过查找(可能是二分)获取当前根节点下标/或用哈希表提前存(比如已知前序/中序/后序中的两个构造二叉树),然后左侧数组递归,右侧数组递归,所以递归参数传递当前数组左右边界

2.题目描述
3.学习经验
1. 108. 将有序数组转换为二叉搜索树(简单,学习)

108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)

思想

1.给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。

2.用左右边界递归,而不是把左数组和右数组作为参数传递

代码
复制代码
class Solution {
public:
    TreeNode* build(vector<int>& nums, int l, int r) {
        // [l,r]
        if (r < l)
            return nullptr;
        int mid = l + ((r - l) >> 1);
        // [l,mid),[mid+1,r]
        return new TreeNode(nums[mid], build(nums, l, mid - 1),
                            build(nums, mid + 1, r));
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return build(nums, 0, nums.size() - 1);
    }
};
2. 654. 最大二叉树(中等)

654. 最大二叉树 - 力扣(LeetCode)

思想

1.给定一个不重复的整数数组 nums最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边子数组前缀上 构建左子树。
  3. 递归地在最大值 右边子数组后缀上 构建右子树。
    返回 nums 构建的 最大二叉树
代码
复制代码
class Solution {
public:
    TreeNode* bulid(vector<int>& nums,int l,int r){
        // [l,r]
        if(r<l) return nullptr;
        int maxId=l;
        for(int i=l;i<=r;++i){
            if(nums[i]>nums[maxId]) maxId=i;
        }
        // [l,maxId),[maxId+1,r]
        return new TreeNode(nums[maxId],
            bulid(nums,l,maxId-1),
            bulid(nums,maxId+1,r)
        );
    }
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        return bulid(nums,0,nums.size()-1);
    }
};
3. 998. 最大二叉树II(中等)

998. 最大二叉树 II - 力扣(LeetCode)

思想

1.最大树 定义:一棵树,并满足:其中每个节点的值都大于其子树中的任何其他值。

给你最大树的根节点 root 和一个整数 val

就像 之前的问题 那样,给定的树是利用 Construct(a) 例程从列表 aroot = Construct(a))递归地构建的:

  • 如果 a 为空,返回 null
  • 否则,令 a[i] 作为 a 的最大元素。创建一个值为 a[i] 的根节点 root
  • root 的左子树将被构建为 Construct([a[0], a[1], ..., a[i - 1]])
  • root 的右子树将被构建为 Construct([a[i + 1], a[i + 2], ..., a[a.length - 1]])
  • 返回 root
    请注意,题目没有直接给出 a ,只是给出一个根节点 root = Construct(a)
    假设 ba 的副本,并在末尾附加值 val。题目数据保证 b 中的值互不相同。
    返回 Construct(b)
    2.由例子可以看出:
    a = [2,1,5,4], b = [2,1,5,4,3]
    就是在数组后面插入一个数val,然后来更新树,再由最大树构造性质,只需判断根节点和右子树
代码
复制代码
class Solution {
public:
    TreeNode* insertIntoMaxTree(TreeNode* root, int val) {
        if (root == nullptr)
            return new TreeNode(val);
        if (root->val < val) {
            return new TreeNode(val, root, nullptr);
        }
        TreeNode* newR = insertIntoMaxTree(root->right, val);
        root->right = newR; // 更新右子树
        return root;
    }
};
4. 1008. 前序遍历构造二叉搜索树(中等)

1008. 前序遍历构造二叉搜索树 - 力扣(LeetCode)

思想
代码
复制代码
class Solution {
public:
    TreeNode* build(vector<int>& preorder, int l, int r) {
        // [l,r]
        if (l > r)
            return nullptr;
        int tmpL = l, tmpR = r, lEnd = r + 1;
        // 查找
        while (tmpL <= tmpR) {
            int mid = tmpL + ((tmpR - tmpL) >> 1);
            if (preorder[mid] > preorder[l]) {
                lEnd = mid;
                tmpR = mid - 1;
            } else
                tmpL = mid + 1;
        }
        // [l+1,lEnd),[lEnd,r]
        return new TreeNode(preorder[l], build(preorder, l + 1, lEnd - 1),
                            build(preorder, lEnd, r));
    }
    TreeNode* bstFromPreorder(vector<int>& preorder) {
        return build(preorder, 0, preorder.size() - 1);
    }
};
5. 1382. 将二叉搜索树变平衡(中等)

1382. 将二叉搜索树变平衡 - 力扣(LeetCode)

思想

1.给你一棵二叉搜索树,请你返回一棵 平衡后 的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。如果有多种构造方法,请你返回任意一种。

如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二叉搜索树是 平衡的

2.先中序遍历二叉搜索树得到数组,然后再用数组构造平衡二叉搜索树

代码
复制代码
class Solution {
public:
    vector<int> nums;
    void dfs(TreeNode* cur) {
        if (cur == nullptr)
            return;
        dfs(cur->left);
        nums.push_back(cur->val);
        dfs(cur->right);
    }
    TreeNode* build(vector<int>& nums, int l, int r) {
        // [l.r]
        if (l > r)
            return nullptr;
        int mid = l + ((r - l) >> 1);
        return new TreeNode(nums[mid], build(nums, l, mid - 1),
                            build(nums, mid + 1, r));
    }
    TreeNode* balanceBST(TreeNode* root) {
        dfs(root);
        return build(nums, 0, nums.size() - 1);
    }
};
6. 2196. 根据描述创建二叉树(中等,学习)

2196. 根据描述创建二叉树 - 力扣(LeetCode)

思想

1.给你一个二维整数数组 descriptions ,其中 descriptions[i] = [parenti, childi, isLefti] 表示 parentichildi二叉树 中的 父节点 ,二叉树中各节点的值 互不相同 。此外:

  • 如果 isLefti == 1 ,那么 childi 就是 parenti 的左子节点。
  • 如果 isLefti == 0 ,那么 childi 就是 parenti 的右子节点。
    请你根据 descriptions 的描述来构造二叉树并返回其 根节点
    测试用例会保证可以构造出 有效 的二叉树。
    2.本题就是建树+找根,建树没有问题,找根用的并查集,其实没必要,根节点入度为0(重要性质),所以建树时记录入度
代码

我的代码:

复制代码
class Solution {
public:
    map<int, TreeNode*> valToPoint;
    map<int, int> valToFa;
    int find(int x) {
        if (x != valToFa[x])
            valToFa[x] = find(valToFa[x]);
        return valToFa[x];
    }
    void merge(int par, int chi) {
        int parFa = find(par), parChi = find(chi);
        if (parFa == parChi)
            return;
        valToFa[parChi] = parFa;
    }
    TreeNode* createBinaryTree(vector<vector<int>>& descriptions) {
        TreeNode* res;
        for (auto& des : descriptions) {
            int par = des[0], chi = des[1], isL = des[2];
            TreeNode *parPoint = nullptr, *chiPoint = nullptr;
            auto isPar = valToPoint.find(par), isChil = valToPoint.find(chi);
            if (isPar != valToPoint.end()) {
                parPoint = valToPoint[par];
            } else {
                parPoint = new TreeNode(par);
                valToPoint[par] = parPoint;
                valToFa[par] = par;
            }
            if (isChil != valToPoint.end()) {
                chiPoint = valToPoint[chi];
            } else {
                chiPoint = new TreeNode(chi);
                valToPoint[chi] = chiPoint;
                valToFa[chi] = chi;
            }
            if (isL)
                parPoint->left = chiPoint;
            else
                parPoint->right = chiPoint;
            merge(par, chi);
        }
        return valToPoint[find(descriptions[0][0])];
    }
};

入度:

复制代码
class Solution {
public:
    map<int, TreeNode*> valToPoint;
    map<int, int> inDeg;
    TreeNode* createBinaryTree(vector<vector<int>>& descriptions) {
        TreeNode* res;
        for (auto& des : descriptions) {
            int par = des[0], chi = des[1], isL = des[2];
            if (!valToPoint.count(par)) {
                valToPoint[par] = new TreeNode(par);
            }
            if (!valToPoint.count(chi)) {
                valToPoint[chi] = new TreeNode(chi);
            }
            if (isL)
                valToPoint[par]->left = valToPoint[chi];
            else
                valToPoint[par]->right = valToPoint[chi];
            ++inDeg[chi];
        }
        for (auto& x : valToPoint) {
            int val = x.first;
            TreeNode* point = x.second;
            if (inDeg[val] == 0)
                return point;
        }
        return nullptr;
    }
};
7. 105. 从前序与中序遍历序列构造二叉树(中等)

105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

思想

1.给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历 ,请构造二叉树并返回其根节点。

2.查找获取当前根节点下标需要前序数组,根节点值就是当前遍历到的前序数组值

3.优化:此题查找本身是给定值找下标,且一定存在,则预先通过哈希表预处理,每次递归查找时间从O(n)变成O(1)

代码
复制代码
class Solution {
public:
    int preId=0,n;
    TreeNode* build(vector<int>& preorder,vector<int>& inorder,int inL,int inR){
        // [inL,inR]
        if(inR<inL || preId>=n) return nullptr;
        int target=preorder[preId++];
        int tmpRes=inL;
        while(tmpRes<=inR){
            if(inorder[tmpRes]==target) break;
            ++tmpRes;
        }
        return new TreeNode(target,build(preorder,inorder,inL,tmpRes-1),build(preorder,inorder,tmpRes+1,inR));
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        n=preorder.size();
        return build(preorder,inorder,0,n-1);
    }
};

哈希表预处理:

复制代码
class Solution {
public:
    int preId = 0, n;
    map<int, int> valToId;
    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int inL,
                    int inR) {
        // [inL,inR]
        if (inR < inL || preId >= n)
            return nullptr;
        int target = preorder[preId++];
        int tmpRes = valToId[target];
        return new TreeNode(target, build(preorder, inorder, inL, tmpRes - 1),
                            build(preorder, inorder, tmpRes + 1, inR));
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        n = preorder.size();
        for (int i = 0; i < n; ++i)
            valToId[inorder[i]] = i;
        return build(preorder, inorder, 0, n - 1);
    }
};
8. 106. 从中序与后序遍历序列构造二叉树(中等)

106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

思想

1.给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

2.与[[十五.二叉树#7. 105. 从前序与中序遍历序列构造二叉树(中等)]]不同,此题后序数组得从后往前遍历,然后利用中序数组构造得先构造右子树,再构造左子树

代码
复制代码
class Solution {
public:
    map<int, int> valToId;
    int postId, n;
    TreeNode* build(vector<int>& inorder, vector<int>& postorder, int inL,
                    int inR) {
        // [inL,inR]
        if (inL > inR)
            return nullptr;
        int target = postorder[postId--];
        int targetId = valToId[target];
        TreeNode* r = build(inorder, postorder, targetId + 1, inR);
        TreeNode* l = build(inorder, postorder, inL, targetId - 1);
        return new TreeNode(target, l, r);
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        n = inorder.size();
        postId = n - 1;
        for (int i = 0; i < n; ++i)
            valToId[inorder[i]] = i;
        return build(inorder, postorder, 0, n - 1);
    }
};
9. 889. 根据前序和后序遍历构造二叉树(中等)

889. 根据前序和后序遍历构造二叉树 - 力扣(LeetCode)

思想

1.给定两个整数数组,preorderpostorder ,其中 preorder 是一个具有 无重复 值的二叉树的前序遍历,postorder 是同一棵树的后序遍历,重构并返回二叉树。

如果存在多个答案,您可以返回其中 任何 一个。

代码
复制代码
class Solution {
public:
    int preId, postId, n;
    map<int, int> valToId;
    TreeNode* build(vector<int>& preorder, vector<int>& postorder, int postL,
                    int postR) {
        // [postL,postR]
        if (postL > postR)
            return nullptr;
        ++preId;
        if (postL == postR) {
            return new TreeNode(postorder[postL]);
        }
        int target = preorder[preId];
        int targetId = valToId[target];
        // [postL,targetId]:left,[targetId+1,postR-1]:right
        return new TreeNode(
            postorder[postR], build(preorder, postorder, postL, targetId),
            build(preorder, postorder, targetId + 1, postR - 1));
    }
    TreeNode* constructFromPrePost(vector<int>& preorder,
                                   vector<int>& postorder) {
        n = preorder.size();
        preId = 0, postId = n - 1;
        for (int i = 0; i < n; ++i)
            valToId[postorder[i]] = i;
        return build(preorder, postorder, 0, n - 1);
    }
};

十一、插入/删除节点

1.套路

1.插入/删除节点都要改变当前节点的左子树或右子树,所以递归返回值是TreeNode*,要赋值给root->left,root->right

2.题目描述
3.学习经验
1. 701. 二次搜索树中的插入操作(中等)

701. 二叉搜索树中的插入操作 - 力扣(LeetCode)

思想

1.给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意 ,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果

代码
复制代码
class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == nullptr)
            return new TreeNode(val);
        if (root->val < val)
            root->right = insertIntoBST(root->right, val);
        else if (root->val > val)
            root->left = insertIntoBST(root->left, val);
        return root;
    }
};
2. 450. 删除二次搜索树中的节点(中等,学习)

450. 删除二叉搜索树中的节点 - 力扣(LeetCode)

思想

1.给定一个二叉搜索树的根节点 root 和一个值 key ,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。
    2.思路为:
    根据二叉搜索树的性质:
  • 如果目标节点大于当前节点值,则去右子树中删除;
  • 如果目标节点小于当前节点值,则去左子树中删除;
  • 如果目标节点就是当前节点,分为以下三种情况:
    • 其无左子:其右子顶替其位置,删除了该节点;
    • 其无右子:其左子顶替其位置,删除了该节点;
    • 其左右子节点都有:其左子树转移到其右子树的最左节点的左子树上,然后右子树顶替其位置,由此删除了该节点。(最难的 )(如下图所示)
      ![[450. 删除二次搜索树中的节点.png]]
代码
复制代码
class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == nullptr)
            return nullptr;
        if (root->val > key) { // 去左子树删除,左子树改变
            root->left = deleteNode(root->left, key);
        } else if (root->val < key) { // 去右子树删除,右子树改变
            root->right = deleteNode(root->right, key);
        } else {                         // 删当前节点
            if (root->left == nullptr) { // 左子树为空,右子树替代
                return root->right;
            }
            if (root->right == nullptr) { // 右子树为空,左子树替代
                return root->left;
            }
            TreeNode* tmp = root->right;
            // 找右子树最左节点
            while (tmp->left != nullptr)
                tmp = tmp->left;
            tmp->left = root->left;
            root = root->right;
        }
        return root;
    }
};
3. 669. 修剪二叉搜索树(中等)

669. 修剪二叉搜索树 - 力扣(LeetCode)

思想

1.给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

代码
复制代码
class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (root == nullptr)
            return nullptr;
        if (root->val < low) {
            root->left = nullptr;          // 抛弃左
            root->right = trimBST(root->right, low, high); 
            // 修剪右
            root = root->right;            // 赋值右
        } else if (root->val > high) {
            root->right = nullptr;         // 抛弃右
            root->left = trimBST(root->left, low, high); 
            // 修剪左
            root = root->left;             // 赋值左
        } else {
            root->left = trimBST(root->left, low, high);   
            // 修剪左
            root->right = trimBST(root->right, low, high); 
            // 修剪右
        }
        return root;
    }
};

更简单一点:

复制代码
class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (root == nullptr)
            return nullptr;
        if (root->val < low) {
            return trimBST(root->right, low, high); 
            // 抛弃左,返回修剪右
        } else if (root->val > high) {
            return trimBST(root->left, low, high); 
            // 抛弃右,返回修剪左
        } else {
            root->left = trimBST(root->left, low, high);   
            // 修剪左
            root->right = trimBST(root->right, low, high); 
            // 修剪右
        }
        return root;
    }
};
相关推荐
liu****4 小时前
20.哈希
开发语言·数据结构·c++·算法·哈希算法
夏鹏今天学习了吗5 小时前
【LeetCode热题100(47/100)】路径总和 III
算法·leetcode·职场和发展
smj2302_796826525 小时前
解决leetcode第3721题最长平衡子数组II
python·算法·leetcode
m0_626535205 小时前
力扣题目练习 换水问题
python·算法·leetcode
第六五5 小时前
DPC和DPC-KNN算法
人工智能·算法·机器学习
一匹电信狗5 小时前
【LeetCode_160】相交链表
c语言·开发语言·数据结构·c++·算法·leetcode·stl
再卷也是菜6 小时前
C++篇(14)二叉树进阶算法题
c++·算法
小邓儿◑.◑6 小时前
贪心算法 | 每周8题(三)
算法·贪心算法
2401_841495646 小时前
【数据结构】最长的最短路径的求解
java·数据结构·c++·python·算法·最短路径·图搜索