代码随想录算法训练营第二十天| 235. 二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点


一、LeetCode 235. 二叉搜索树的最近公共祖先

题目链接:LeetCode 235. 二叉搜索树的最近公共祖先

文章讲解:代码随想录
视频讲解:自底向上查找,有点难度! | LeetCode:236. 二叉树的最近公共祖先

思路:

 判断两个结点在二叉树中的最近公共祖先,笔者的算法思路为设置全局变量ances记录最近的公共祖先结点,设计递归函数searchOffspring自底向上地遍历结点,递归函数返回bool值来判断子树中是否含有pq

 递归函数主体中,判断当前结点是否是最近祖先有两种情况:

  1. 当前结点的左右子树分别出现过pq
  2. 当前结点为pq中的一个,另一个在当前结点的子树中

递归终止条件为遍历到空节点,此时返回false;

C++代码

/**
 * 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* ances; //全局变量记录最近祖先结点
    bool searchOffspring(TreeNode* node, TreeNode* p, TreeNode* q){
        //使用bool值记录当前结点的子树是否出现过p或q
        if(!node) return false;
        bool left = searchOffspring(node->left, p, q);
        bool right = searchOffspring(node->right, p, q);
        if(node == p || node == q){ //若当前结点和p、q相同
            if(left || right){
                ances = node;
                return false; //防止最近祖先上方结点重复赋值
            }
            return true;
        }
        else if(left && right){ //当前结点的左右子树出现过p、q,则为最近祖先。
            ances = node;
            return false;
        }
        return left || right;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        searchOffspring(root, p, q);
        return ances;
    }
};

二、LeetCode 701.二叉搜索树中的插入操作

题目链接:LeetCode 701.二叉搜索树中的插入操作

文章讲解:代码随想录
视频讲解:原来这么简单? | LeetCode:701.二叉搜索树中的插入操作

思路

 给二叉搜索树中插入新元素,由于题目没有要求二叉搜索树平衡的条件,那么只需要按照二叉搜索的方式递归遍历,直到遍历到第一个空的孩子结点,把插入的新元素放到这个孩子结点的位置即可。
在这里插入图片描述

C++代码

/**
 * 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) {
        TreeNode* subroot = root;
        while(subroot){
            if(val < subroot->val){
                if(subroot->left){
                    subroot = subroot->left;
                    continue;
                }
                else{
                    TreeNode* node = new TreeNode(val);
                    subroot->left = node;
                    return root;
                }
            }
            else{
                if(subroot->right){
                    subroot = subroot->right;
                    continue;
                }
                else{
                    TreeNode* node = new TreeNode(val);
                    subroot->right = node;
                    return root;
                }
            }

        }
        TreeNode* node = new TreeNode(val);
        return node;
    }
};

三、LeetCode 450.删除二叉搜索树中的节点

题目链接:LeetCode 450.删除二叉搜索树中的节点

文章讲解:代码随想录
视频讲解:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点

思路

 本题也需要实现二叉搜索树的一个基本维护操作,但与插入操作不同的是,删除结点一般都是非叶子结点,意味着不能只单纯实现删除一个结点操作,还需要调整删除结点后,该结点左右孩子的结构,并连接到原来的二叉树上。

 我们以二叉搜索树中一个含有左右孩子的一般结点为例。

 首先,删除结点后,该结点的左右孩子结构如何调整?我们注意到,结点的左子树是包含所有小于该结点的数,右子树包含所有大于该结点的树,那么左子树的所有元素一定比右子树的最小元素小,借助这个特性,我们可以使用如下策略调整结构,即:
把左子树的根结点,连接到右子树的最左下方结点(右子树中的最小值)的左孩子位置。

 调整好结构以后,需要考虑连接到原二叉树的问题。由于删除结点无论是他的双亲的左孩子还是右孩子,该结点的子树和该结点的双亲大小关系一定是确定的,所以修改好结构的子树,只需要把子树根结点(上一段右孩子的根节点)连接到双亲即可。

 需要考虑几种特殊情况,当删除结点只有一个孩子结点时,直接连接该孩子结点即可,无需进行结构调整;当删除结点是叶子结点时,只删除结点即可。

C++代码

/**
 * 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* deleteNode(TreeNode* root, int key) {
        if(!root) return root;

        TreeNode* node; //记录需要删除的结点
        TreeNode* subroot = root; //记录删除结点的双亲,便于调整结构

        if(key == root->val){
            if(!root->left){
                return root->right;
            }
            if(!root->right){
                return root->left;
            }
            node = root;
        }
        else{
            node = (key < root->val) ? root->left: root->right;
            while(node){//查找结点
                if(key < node->val){
                    subroot = node;
                    node = node->left;
                } 
                else if(key > node->val){
                    subroot = node;
                    node = node->right;
                } 
                else break;
            }
        }

        if(node){ //删除结点
            TreeNode* inst;
            if(node->right){
                inst = node->right;
                while(inst->left){ 
                //把删除结点的左子树,放在删除结点右子树的最左边叶子位置
                    inst = inst->left;           
                }
                inst->left = node->left;
                inst = node->right;
            } 
            else{
                inst = node->left;
            }          
            if(node->val < subroot->val) subroot->left = inst;
            else subroot->right = inst;
            if(node == root) root = inst;
        }
        return root;
    }
};

总结

 对于二叉搜索树中的删除结点操作应加强复习。


文章图片来源:代码随想录 (https://programmercarl.com/)

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值