代码随想录算法训练营day20

3 阅读4分钟

236 最近公共祖先 1.依旧采用三段法设计递归思路 2.重点要理清楚公共祖先的具体条件,不能死板的根据路径同时到达去找,而是通过递归返回值的思路确定在哪条路径存在,然后就返回哪条路径 3.关于上层递归值为什么不会覆盖,需要仔细思考。实际最近公共祖先的判断仅仅只能触发一次!!!


class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        //使用递归求二叉树最近公共祖先
        //返回值和传入参数如题设
        //确定终止条件和回溯情况
        if(root==p||root==q||root== nullptr) return root;
        //确定单层递归逻辑
        //通过返回值判断p,q是否分别存在祖先结点的子树中
        TreeNode* leftnode=lowestCommonAncestor(root->left,p,q);
        TreeNode* rightnode=lowestCommonAncestor(root->right,p,q);
        //当p,q分别存在于该节点的左右子树(包括该结点)时,返回该结点
        // 情况1:左右子树都找到目标节点 → 当前root就是最近公共祖先
        if (leftnode != nullptr && rightnode != nullptr) {
            return root;//这里解释一下为什么上层递归不会新生成root覆盖,最近公共祖先
                        //必为其祖先的左右孩子,因此当出现最近公共祖先时,此判断仅仅触发一次,后续都为情况2或者情况3
        }
        // 情况2:仅左子树找到 → 返回左子树的结果(目标节点/更上层的公共祖先)
        else if (leftnode != nullptr && rightnode == nullptr) {
            return leftnode;
        }
        // 情况3:仅右子树找到 → 返回右子树的结果
        else if (leftnode == nullptr && rightnode != nullptr) {
            return rightnode;
        }
        // 情况4:左右都没找到 → 返回空(补全默认返回值,解决编译错误)
        else {
            return nullptr;
        }
    }
};

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


class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        //依旧采用递归方式,这里可以利用二叉搜索树有序的特点,利用区间快速获得最近公共祖先
       
         if (root == nullptr) return root;

        // 情况1:p和q都在当前节点的左子树(当前节点值 > p/q的值)
        if (root->val > p->val && root->val > q->val) {
            // 递归左子树,直接返回结果(BST特性保证无需额外判断)
            return lowestCommonAncestor(root->left, p, q);
        }
        // 情况2:p和q都在当前节点的右子树(当前节点值 < p/q的值)
        else if (root->val < p->val && root->val < q->val) {
            // 递归右子树,直接返回结果
            return lowestCommonAncestor(root->right, p, q);
        }
        // 情况3:p和q分别在当前节点的左右两侧,或当前节点是p/q → 就是最近公共祖先
        else {
            return root;
        }
    }
};

701 二叉搜索树的插入

1.采用递归方式代码更加简洁 2.使用递归时在单层递归时要严谨考虑是不是需要返回值,返回值的意义是什么


class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        //采用递归法执行
        //递归终止条件,遍历到叶子结点的空结点时赋值
        if(root==nullptr) {
            TreeNode* tmp=new TreeNode(val);//构造新结点
            return tmp;
        }
        //单层逻辑
        if(root->val==val) return root;
        if(root->val>val) root->left=insertIntoBST(root->left,val);//向左插入递归
        if(root->val<val) root->right=insertIntoBST(root->right,val);//向右插入递归
        return root;//保留根结点并返回      
    }
};

450 二叉树的删除 具体难点在于删除的结点的几个特殊情况判断,个人建议阅读邓俊辉数据结构二叉搜索树的删除,应当使用后继结点处理,并且防止内存泄漏


class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        //使用递归方式来进行删除
        //底层递归终止条件
        if(root==nullptr) return root;
        //单层递归逻辑
        //当root值大于key值时
        if(root->val>key) 
         root->left=deleteNode(root->left,key);
        else if(root->val<key) //小于key值时
         root->right=deleteNode(root->right,key);
        else if(root->val==key){
            //分多种情况展开讨论
            //root为叶子结点时,直接删除
            if(root->right==nullptr&&root->left==nullptr)
             {
                delete root;
                return nullptr;
             }
            //第二种情况,仅存在左孩子
            else if(root->right==nullptr){
                TreeNode* temp = root->left; // 保存左孩子指针
                delete root; // 释放原节点(修复内存泄漏)
                return temp; // 返回左孩子,让上层挂载
            }
            //第三种情况,仅存在右孩子
            else if(root->left==nullptr){
                TreeNode* temp = root->right; // 保存右孩子指针
                delete root; // 释放原节点(修复内存泄漏)
                return temp; // 返回右孩子,让上层挂载
            }
            //第四种情况,存在左右孩子时,使用最小大于root值的结点替代
            else{
                TreeNode* tmp=root->right;
                //替代结点可为右子树的最左侧孩子,也可以是右孩子,此处是右孩子
                while(tmp->left!=nullptr){
                    tmp=tmp->left;
                }
                //此时tmp即最左侧孩子,采用孤立方法讲原root隔离
                //讲root的左孩子变成tmp的左孩子
                tmp->left=root->left;
                //使用deletenode记录将要被删除的root
                TreeNode* deletenode=root;
                //使用右孩子替代root,因为采用的递归,所以会通过返回值自动与父节点联系
                root=root->right;
                delete deletenode;
                return root;
            }

        }    
        return root;     
    }
};