代码随想录Day22 | 235. 二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点
二叉搜索树的最近公共祖先
文档讲解:代码随想录
视频讲解: 二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先
状态
搜索树是有序的,也就是说如果是祖先其val一定在p和q之间,且一定是从上往下遍历第一个在之间的值,且是唯一一个
迭代:对root节点循环,当其值比两个值都小那么就向右子树取值,当其值比两个数都大就向左子树取值,知道遍历到空节点,如果没有返回那么就返回null
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while(root)
{
if(root->val > p->val && root->val > q->val)
{
root = root->left;
}
else if(root->val < p->val && root->val < q->val)
{
root = root->right;
}
else return root;
}
return nullptr;
}
};
递归:原理和迭代一样,只不过遍历中如果遇到了在中间的情况直接返回就不需要再遍历了
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==nullptr) return root;
if(root->val > p->val && root->val > q->val)
{
return lowestCommonAncestor(root->left,p,q);
}
else if(root->val < p->val && root->val < q->val)
{
return lowestCommonAncestor(root->right,p,q);
}
else
{
return root;
}
}
};
二叉搜索树中的插入操作
文档讲解:代码随想录
视频讲解: 原来这么简单? | LeetCode:701.二叉搜索树中的插入操作
状态
迭代,思路和上一题类似,当当前节点大于目标值,向左移动反之向右移动,但还需要记录移动前的节点值,是为了遍历结束后添加节点。
这种方法不保证平衡,只在最后面添加
//判断数据与当前父节点的大小
//直到遇到空节点进行插入
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
TreeNode* addNode = new TreeNode(val);
if(root == nullptr)
{
root = addNode;
return root;
}
TreeNode* cur = root;
TreeNode* pre = cur;
while(cur)
{
pre = cur;
if(cur->val > val)
{
cur = cur->left;
}
else
{
cur = cur->right;
}
}
if(pre->val > val) pre->left = addNode;
else pre->right = addNode;
return root;
}
};
递归:利用返回值处理节点的添加。节点的添加无非就是root->left = node或者root->right = node,所以我们可以为递归函数添加返回值,这样就可以在归的过程中将子树全部接到父节点上了。
删除二叉搜索树中的节点
文档讲解:代码随想录
视频讲解: 调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点
状态
主要在于删除当前节点的情况分类
- 没有k值即没有要删除的节点,那么直接返回即可
- 删除的节点是叶子节点,那么直接删除返回即可
- 删除的节点的父节点,但其只有左子树或者右子树,那么直接将上一个节点的指向改变即可
- 删除的节点左右子树都存在,有两种处理方式:第一种将左节点接上去,然后右节点和其子树接到左节点的左子树的最右下角。或者移动右节点,将左子树接到右子树的最左下角。
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root == nullptr) return root;
//向左递归
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 == nullptr&&root->right==nullptr) return nullptr;
else if(root->left && root->right==nullptr)
{
root = root->left;
return root;
}
else if(root->left == nullptr&&root->right)
{
root = root->right;
return root;
}
//左右子树都存在
else
{
TreeNode* temp = root->right;
root = root->left;
//使用临时变量来搜索右子树的插入节点
//避免改变要返回的节点地址
TreeNode* cur = root;
while(cur->right)
{
cur = cur->right;
}
cur->right = temp;
return root;
}
}
return root;
}
};
普通二叉树的删除节点
普通二叉树的删除就需要遍历整个树才能说删除成功。相当于就没有了小于和大于的判断而是对每个子节点都进行了递归操作。相等的逻辑还是一样的,只不过我们还是将其放在了最后。
还有一种简化移动过程,其实不是移动而是交换,想到于直接把左子树或者右子树的小于两个子树的节点的值与待删除节点的值进行交换,由于全部遍历,交换过后,但删除的值肯定还会被遇到,但此时它最多只有一个子树所以直接覆盖即可
这种方法就需要使用前序遍历,先对当前中节点进行判断移动,否则会出现删除不了的情况
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root == nullptr) return root;
if(root->val == key)
{
//确定与那个子树交换
if(root->right == nullptr)
{
return root->left;
}
TreeNode* cur = root->right;
while(cur->left)
{
cur = cur->left;
}
//此时的cur左子树为空
swap(root->val,cur->val);
}
//向左递归
root->left = deleteNode(root->left,key);
//向右递归
root->right = deleteNode(root->right,key);
return root;
}
};