(22/60)二叉搜索树的最近公共祖先、二叉搜索树的插入操作、删除二叉搜索树的节点

二叉搜索树的最近公共祖先

leetcode:235. 二叉搜索树的最近公共祖先

递归法

思路

搜索树节点是有序的,若节点值大于p、q则在右子树查询;若节点值小于p、q则在左子树查询;其他情况(节点值在两者之间、节点值等于p值或q值),node就是最近的公共祖先。

复杂度分析

时间复杂度:O(logN)。每次递归减少一半规模。

空间复杂度:递归栈深度。最好O(logN),最差O(N)。

注意点

  1. 为甚麽遇到的第一个就是最近公共祖先:cur在p和q之间,p、q分别在cur的两个子树上。向下一步,都会使得子树不再包含p、q。

代码实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

class Solution {
public:
    // 公共节点一定在p、q之间,递归遍历
    TreeNode* ancestor;
    void traversal(TreeNode* node,TreeNode* p,TreeNode* q){
        if(!node) return;

        if(node->val > p->val && node->val > q->val) traversal(node->left,p,q);
        else if(node->val < p->val && node->val < q->val) traversal(node->right,p,q); 
        else ancestor = node;	// 因为祖先一定存在,可以这样写
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        traversal(root,p,q);
        return ancestor;
    }
};

迭代法

思路

cur比两个都大,左移;cur比两个都小,右移;其他情况cur就是最近公共祖先。

复杂度分析

时间复杂度:O(logN)。

空间复杂度:O(1)。无额外辅助空间。

注意点

代码实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

class Solution {
public:
    // 试试迭代
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root) return NULL;
        TreeNode* cur = root;
        while(cur){
            if(cur->val > p->val && cur->val > q->val) cur = cur->left;
            else if(cur-> val < p->val && cur->val < q->val) cur = cur->right;
            else return cur;
        }        
        return NULL;
    }
};

二叉搜索树的插入

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

递归法

思路

就记得一句——“对搜索树遍历是对搜索树的侮辱”。

  1. 节点为空时,返回构造的新节点。

  2. val值小于节点值时,节点左子节点接收左子节点的递归;

    val值大于节点值时,节点右子节点接收右子节点的递归。

  3. (完成左右子树的构造后)最后返回root。

复杂度分析

时间复杂度:O(logN)。

空间复杂度:递归栈深度。最好O(logN),最差O(N)。

注意点

代码实现

/**
 * 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:
    // 试下递归
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if(!root) return new TreeNode(val); // 节点为空时,返回构造的新节点

        if(val < root->val) root->left = insertIntoBST(root->left,val); // 接收节点
        if(val > root->val) root->right = insertIntoBST(root->right,val);

        return root;	// root左右子树构造完成后返回root
    }
};

迭代法

思路

见缝插针。根据题设,所有节点数值不重复,所以一定能找到位置插入。

只要根据搜索树规则,移动到指定位置,且该位置空着,插入新节点即可;否则,向下移动继续循环。

复杂度分析

时间复杂度:O(logN)。和二叉搜索树的查找一样。

空间复杂度:O(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 {
public:
    // 在后面加的方式
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if(!root) return new TreeNode(val);
        TreeNode* cur = root;
        while(cur){	// cur不为空
            if(val < cur->val){ // val小于cur的值
                if(cur->left) cur = cur->left;	// 如果cur->left被占了,cur继续向下移动
                else{	// 否则,cur->left就为构造新node,跳出循环。
                    cur->left = new TreeNode(val);
                    break;
                }
            }else if(val > cur->val){
                if(cur->right) cur = cur->right;
                else{
                    cur->right = new TreeNode(val);   
                    break;
                }
            }
        }

        return root;
    }
};

删除二叉搜索树中的节点

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

思路

找到key和没找到key两大类,总共五种情况。其中2、3、4、5是左、右节点空、非空状态的排列组合的四种情况。

  1. 没找到,向上返回NULL。

  2. 是叶子节点(左右都空),删除(释放)节点,向上返回NULL。

  3. 左不空右空,删除节点,向上返回左子树。

  4. 左空右不空,删除节点,向上返回右子树。

  5. 左右都不空,找一棵树为主另一颗为副,先进行副子树依附主子树的转移,再按照3或4中的情况处理。

    以右子树为主为例,左子树需要依附到一个比root大但最小的子节点上:

    1. 找到右子树的最左子节点;
    2. 令右子树的最左子节点链接上左子树root->left
    3. 删除节点;
    4. 向上返回右子树。

复杂度分析

时间复杂度:O(logN)。搜索树,懂得都懂。

空间复杂度:递归栈深度。平衡时O(logN),极倾斜时O(N)。

注意点

代码实现

/**
 * 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:
    // 五种情况,
    // 其中2、3、4、5是左、右节点空、非空状态的排列组合的四种情况
    // 1. 没找到
    // 2. 是叶子节点
    // 3. 左不空右空
    // 4. 左空右不空
    // 5. 左右都不空
    TreeNode* deleteNode(TreeNode* root, int key) {
        if(!root) return NULL;
        if(root->val == key){
            if(!root->left && !root->right){
                delete root;
                return NULL;
            }else if(root->left && !root->right){
                TreeNode* leftNode = root->left;
                delete root;
                return leftNode;
            }else if(!root->left && root->right){
                TreeNode* rightNode = root->right;
                delete root;
                return rightNode;
            }else{	// 以右子树为主
                TreeNode* cur = root->right;
                while(cur->left){   // 找右节点中最左边的子节点(比root大的最小节点)
                    cur = cur->left;
                }
                cur->left = root->left;
                cur = root->right;  // cur指向root的右子树
                delete root; 
                return cur; // 返回原root右子树
            }
        }
        
        // 搜索树进行下去
        if(key < root->val) root->left = deleteNode(root->left,key);
        if(key > root->val) root->right = deleteNode(root->right,key);
        
        return root;
    }
};

以左子树为主的版本:

// ...

else{ // 试试以左子树为主
    TreeNode* cur = root->left;
    while(cur->right) cur = cur->right; // 在左树中找比root小值里的最大值,将root的右子树挂在上面
    cur->right = root->right; // 把root右子树挂在左子树的最大节点上
    cur = root->left;
    delete root;
    return cur;
}

// ...
posted @ 2024-02-20 00:08  Tazdingo  阅读(1357)  评论(0)    收藏  举报