二叉搜索树(2)

1,二叉搜索树的插入

来源:701. 二叉搜索树中的插入操作

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        TreeNode *t = new TreeNode(val);
        if (root == NULL) return t;
        // 作为一个叶子节点插入树中
        TreeNode *p = root;
        while (p) {
            if (p->val < val) {
                if (p->right == NULL) {
                    p->right = t;
                    break;
                } else {
                    p = p->right;
                }
            } else {
                if (p->left == NULL) {
                    p->left = t;
                    break;
                } else {
                    p = p->left;
                }
            }
        }
        return root;
    }
};

2,有序数组构造二叉搜索树

来源:108. 将有序数组转换为二叉搜索树

转换的二叉搜索树可以有很多种,返回其中一种即可。思路:中间数字作为根节点,两边数字作为左右子树。

class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return create(nums, 0, nums.size() - 1);
    }
    // 区间[l, r]
    TreeNode* create(const vector<int>& nums, int l, int r) {
        if (l > r) return NULL;
        int mid = (l + r) / 2;
        TreeNode *n = new TreeNode(nums[mid]);
        n->left = create(nums, l, mid - 1);
        n->right = create(nums, mid + 1, r);
        return n;
    }
};

3,前序遍历构造二叉搜索树

来源:1008. 前序遍历构造二叉搜索树

与题3不同,本题构造的二叉搜索树只有一种。思路:第1个节点为根,后面数字分成2部分,大于节点1的部分是右子树,其余为左子树。

class Solution {
public:
    TreeNode* bstFromPreorder(vector<int>& preorder) {
        return create(preorder, 0, preorder.size() - 1);
    }
    // 第1个节点为根,后面数字分成2部分,大于节点1的部分是右子树,其余为左子树
    TreeNode* create(const vector<int>& nums, int l, int r) {
        if (l > r) return NULL;
        TreeNode *n = new TreeNode(nums[l]);
        int mid = l + 1;
        while (mid <= r) {
            if (nums[mid] > nums[l]) break;
            mid++;
        }
        n->left = create(nums, l+1, mid - 1);
        n->right = create(nums, mid, r);
        return n;
    }
};

4,n个节点生成不同二叉搜索树的数量

来源:96. 不同的二叉搜索树

动态规划,将每个节点都作为根节点,左边数字作为左子树,右边数字作为右子树,可以组成树的数量有count(左子树) * count(右子树)。举例说明:

假如有5个数字:1,2,3,4,5,对于第4个数字,左子树有3个节点,可能情况为f(3),右子树有1个节点,可能情况为f(1)。

所以当4为根节点时,共有f(3)*f(1)种不同二叉搜索树。

依次类推:

1为根节点,数量:f(0)*f(4)

2为根节点,数量:f(1)*f(3)

3为根节点,数量:f(2)*f(2)

5为根节点,数量:f(4)*f(0)

当n=5时,数量:f(0)*f(4) + f(1)*f(3) + f(2)*f(2) + f(3)*f(1) + f(4)*f(0)。

所以n时,数量:f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-1)*f(0)

class Solution {
public:
    // 动态规划,f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-1)*f(0)
    int numTrees(int n) {
        if (n <= 2) return n;
        vector<int> f(n+1, 0);

        f[0] = 1;
        f[1] = 1;
        f[2] = 2;
        for (int i = 3; i <= n; i++) {
            int s = 0;
            for (int j = 0; j < i; j++) {
                s += f[j] * f[i-j-1];
            }
            f[i] = s;
        }
        int ret = f[n];
        return ret;
    }
};

5,有序数组构造所有的二叉搜索树

来源:95. 不同的二叉搜索树 II

与题2类似,不同的是,题2只需构造一种可能即可,本题需构造所有的可能,可能数量同题4。

思路1(举例说明):假如有数字序列:1,2,3,...,k,...,n,当根节点为k时,其左子树是节点1,2,3,,,k-1组成树的任意一种,右子树是k+1,...,n组成树的任意一种。

注意:左子树直接复制,右子树在复制的时候将节点累加k。

class Solution {
public:
    vector<vector<TreeNode*>> dp;
    vector<TreeNode*> generateTrees(int n) {
        {
            // n=0时,一颗空树
            vector<TreeNode*> t(1, NULL);
            dp.push_back(t);
        }
        {
            // n=1时,一颗树且只有1个节点,且节点值为1
            vector<TreeNode*> t(1, new TreeNode(1));
            dp.push_back(t);
        }
        if (n <= 1) return dp[n];
        
        for (int i = 2; i <= n; i++) {
            generate(i);
        }
        return dp[n];
    }
    void generate(int k) {
        vector<TreeNode*> result;
        // 每个节点做根, 根节点j,左子树节点个数j-1, 右子树节点个数k-j-1
        for (int j = 1; j <= k; j++) {
            // dp[j-1] * dp[k-j]
            const vector<TreeNode*>& left = dp[j-1];
            const vector<TreeNode*>& right = dp[k-j];
            for (int l = 0; l < left.size(); l++) {
                for (int r = 0; r < right.size(); r++) {
                    TreeNode* root = new TreeNode(j);
                    root->left = copy(left[l], 0);
                    root->right = copy(right[r], j); // 右子树最小值应该为j+1,所以数值差j+1-1=j
                    result.push_back(root);
                }
            }
        }
        dp.push_back(result);
    }

    TreeNode* copy(TreeNode* root, int add_number) {
        if (root == NULL) return NULL;
        TreeNode *ret = new TreeNode(root->val + add_number);
        ret->left = copy(root->left, add_number);
        ret->right = copy(root->right, add_number);
        return ret;
    }
};

思路2:递归方案代码简单易懂,但不容易实现。遇到树的问题,应该先递归,递归实在想不出来,再考虑其他思路

class Solution {
public:
    vector<TreeNode*> generateTrees(int n) {
        return generate(1, n);
    }
    vector<TreeNode*> generate(int start, int end) {
        if (start > end) return vector<TreeNode*>(1, NULL);

        vector<TreeNode*> result;
        for (int i = start; i <= end; i++) {
            // 递归生成左右子树
            vector<TreeNode*> left = generate(start, i - 1);
            vector<TreeNode*> right = generate(i + 1, end);
            for (int l = 0; l < left.size(); l++) {
                for (int r = 0; r < right.size(); r++) {
                    TreeNode* root = new TreeNode(i);
                    root->left = left[l];
                    root->right = right[r];
                    result.push_back(root);
                }
            }
        }
        return result;
    }
};

6,二叉搜索树保留[low,high]的节点

来源:669. 修剪二叉搜索树

通过修剪二叉搜索树,使得所有节点的值在[low, high]中,修剪树不应该改变保留在树中的元素的相对结构。

思路1:先前序遍历树得到范围内的所有节点,再将前序遍历结果转换为一颗二叉搜索树(题3)。

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        vector<int> nums;
        visit(root, low, high, &nums);
        return create(nums, 0, nums.size() - 1);
    }
    void visit(TreeNode* root, int low, int high, vector<int> *ret) {
        if (root == NULL) return;
        if (root->val >= low && root->val <= high) ret->push_back(root->val);
        visit(root->left, low, high, ret);
        visit(root->right, low, high, ret);
    }
    TreeNode* create(const vector<int>& nums, int l, int r) {
        if (l > r) return NULL;
        TreeNode *n = new TreeNode(nums[l]);
        int mid = l + 1;
        while (mid <= r) {
            if (nums[mid] > nums[l]) break;
            mid++;
        }
        n->left = create(nums, l+1, mid - 1);
        n->right = create(nums, mid, r);
        return n;
    }
};

思路2:当前节点如果比high大,说明当前节点及右子树均需要剪掉,在左子树上继续处理;如果比low小,在右子树上继续处理。如果在low和high范围内,左右分别处理。

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (root == NULL) return NULL;
        if (root->val > high) return trimBST(root->left, low, high);
        if (root->val < low) return trimBST(root->right, low, high);
        root->left = trimBST(root->left, low, high);
        root->right = trimBST(root->right, low, high);
        return root;
    }
};

7,二叉搜索树删除某个节点

来源:450. 删除二叉搜索树中的节点

思路:先找到待删除节点,待删除节点有4种情况:

最后一种情况,也可以将右子树最小值min放到root,在右子树上删除min节点。结果不唯一,虽然与期望结果不一样,但依然可以通过。

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == NULL) return NULL;
        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 == NULL) {
                if (root->right == NULL) root = NULL; // 叶节点
                else return root = root->right;
            } else {
                if (root->right == NULL) root = root->left;
                else {
                    // 左子树最大值
                    TreeNode* p = root->left;
                    while (p->right) {
                        p = p->right;
                    }
                    root->val = p->val;
                    root->left = deleteNode(root->left, p->val);
                }
            }
        }
        return root;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值