235. 二叉搜索树的最近公共祖先
这题和昨天的最后一题一样,但是搜索树是有性质的。
只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中则一定可以说明该节点cur就是p 和 q的最近公共祖先。
class Solution {
private:
TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {
if (cur == NULL) return cur;//遍历完二叉树了
// 中
if (cur->val > p->val && cur->val > q->val) { // 左,大于往节点的左子树找
TreeNode* left = traversal(cur->left, p, q);
if (left != NULL) {
return left;
}
}
if (cur->val < p->val && cur->val < q->val) { // 右,小于往节点的右子树找
TreeNode* right = traversal(cur->right, p, q);
if (right != NULL) {
return right;//在子树中找到指定节点就直接返回,否则就是返回null没找到
}
}
return cur;//两种情况都不满足的节点就是我们要找的节点
}
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
return traversal(root, p, q);
}
};
701.二叉搜索树中的插入操作
这题不用多想,总有一种插入方式可以不改变二叉树的结构直接将新节点作为叶子节点插入。
关键就是寻找过程
1.当前节点比value大:往右子树遍历
2.当前节点比value小:往左子树遍历
3.当root==null时,说明已经到底了,需要插入新的节点,这时候我们需要root的上一个节点t1来帮助我们插值。
(1)如果 是在root=root->left;的情况下进入插入新节点的状态时,新的节点肯定插在t1的左边(也就是现在root的位置,但是root已经是null没办法赋值了)
(2)如果 是在root=root->right;的情况下进入插入新节点的状态时,新的节点肯定插在t1的右边(也就是现在root的位置,但是root已经是null没办法赋值了)
class Solution {
public:
TreeNode*t1=nullptr;
void charu(TreeNode* root, int val)
{
while(root!=NULL)
{
if(root->val>val)
{
t1=root;
root=root->left;
if(t1!=nullptr&&root==nullptr)
{
t1->left=new TreeNode(val);
return ;
}
}
else if(root->val<val)
{
t1=root;
root=root->right;
if(t1!=nullptr&&root==nullptr)
{
t1->right=new TreeNode(val);
return ;
}
}
}
return ;
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root==nullptr)
{
TreeNode*t2=new TreeNode(val);
return t2;
}
charu(root,val);
return root;
}
};
450.删除二叉搜索树中的节点
这题在删除的时候需要改变整个树的结构,而且删除的思路也值得学习。
一、首先就是有多少种情况
1.没遍历到,返回null
2.遍历到,删除节点为叶子节点,直接删
3.遍历到,删除节点仅有左/右节点,删掉当前节点,然后让左/右节点取而代之
4.遍历到,删除节点左右节点都有,删除思路如下
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root==nullptr)return nullptr;
if(root->val==key)
{
if(root->left==nullptr&&root->right==nullptr)
{
delete root;
return nullptr;
}
else if(root->left==nullptr&&root->right!=nullptr)
{
auto t1=root->right;
delete root;
return t1;
}
else if(root->left!=nullptr&&root->right==nullptr)
{
auto t1=root->left;
delete root;
return t1;
}
else
{
auto t1=root->right;
auto t2=root->left;
auto t3=root->right;
while(t3->left!=nullptr)
t3=t3->left;
t3->left=t2;
delete root;
return t1;
}
}
if (root->val > key) root->left=deleteNode(root->left,key);
if (root->val < key) root->right=deleteNode(root->right,key);
return root;
}
};
二、删除方式
可以看到,在删除过程中,是直接返回新的节点,而不是在原有节点的值上进行修改的!
举个例子:删除3节点,3节点的右节点为2。
删除思路不是把3节点的值改成2,而是把3delete了然后直接返回节点2的指针!
这样写其实还是有伏笔的
if (root->val > key) root->left=deleteNode(root->left,key);
if (root->val < key) root->right=deleteNode(root->right,key);
return root;
三、返回
最后前序遍历时就用到了这点,首先利用root->val 和 key之间的大小关系确定应该去哪里遍历(左子树还是右子树),而不是单纯的前序遍历。
还有就是return root,这里其实就是为什么要用二、的删除方式(直接返回新节点)
这样的话,如果删除的是头节点,我们也能直接返回新的头节点!用改节点值的思路就很困难了。