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;
}
};