文档讲解:二叉搜索树的最近公共祖先??二叉搜索树的插入操作??删除二叉搜索树中的节点
题目链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/description/
思路:
? ? ? ?这道题目和LeetCode 700并没有太大的不同,直接视为普通二叉树去做就好了。详细思路可以看我昨天的博客。
核心代码:
/**
* 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==p||root==q||root==NULL) return root;
TreeNode* left=lowestCommonAncestor(root->left,p,q);
TreeNode* right=lowestCommonAncestor(root->right,p,q);
if(left&&right) return root;
if(!left) return right;
return left;
}
};
题目链接:https://leetcode.cn/problems/insert-into-a-binary-search-tree/description/
思路:
? ? ? ? 这题很简单,虽然插入方式有很多种,但我们只需要找出一种即可。因此我们让其插入后成为叶子节点即可,不用考虑和其他点的关系,也不用调整树的结构,这是最简单的插入方式。
? ? ? ? 具体来说,我们根据要插入的值在二叉搜索树中进行遍历。 如果这个值比根节点小,就往左找,反之就往右找。当找到空节点时,说明这个值插入后可以在这个位置。在这个位置开个新节点赋进去即可。
核心代码:
/**
* 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) {
if(!root) root=new TreeNode(val);
else if(val<root->val) root->left=insertIntoBST(root->left,val);
else root->right=insertIntoBST(root->right,val);
return root;
}
};
题目链接:https://leetcode.cn/problems/delete-node-in-a-bst/description/
思路:
? ? ? ? 删除树上某个节点,首先我们需要找到这个节点,递归遍历即可。如果没找到,证明没有这个点,自然就更不用考虑删除了。
? ? ? ? 找到这个点后,我们考虑怎么删除它。我们知道这样一件事:删除掉这个点后,以这个点为根节点的子树就和原本那颗树断开了,我们就考虑把它连回去即可,同时注意要维护其二叉搜索树的性质。这样的话我们知道原本树中除这棵子树之外的节点不用调整,因为删除这棵子树中的任何一个值都不影响它们之间的大小关系。所以接下来的问题就是,删除这个点,调整子树结构,然后把新的根节点回溯到上一层赋给上一层的子节点即可。
? ? ? ? 我们把这颗子树单独拎出来讨论,删除根节点后有如下几种情况:
? ? ? ? 1.子树根节点的左右子节点均为空,即子树只有一个点,那么删除后树空了,返回空节点即可,不需要再向下调整了。
? ? ? ? 2.子树根节点的左右子节点一个为空一个不为空,即子树根节点只有一个儿子。这时我们很容易想到,以子树根节点的儿子为根节点的子树,其实本身已经是二叉搜索树了,不需要调整,删除子树根节点不影响其性质。所以我们直接返回这个儿子节点即可,也不需要向下调整了。
? ? ? ? 3.子树根节点的两个儿子均不为空。这时候左儿子的子树中的值小于根节点,右儿子子树中的值大于根节点,我们需要考虑把从哪个儿子把哪个点拿到根节点来,调整拿出点的这个儿子子树的结构即可,另一个不用动,因为原来就满足性质。
? ? ? ? 但是这样做其实不好做,每次需要交换值且进行判断。我们考虑这样一件事,情况 3 能不能变成情况 2 ,或者类似情况 2 。其实是可以的。我们知道这样一件事,要删除这个点的左儿子子树一定是一个二叉搜索树,这是原树性质保证的。那么右儿子子树的任何一个点的值,都比左儿子子树中的值大。我们将左儿子子树挪到右儿子子树的任何一个点的左儿子节点上,都能与其接着满足二叉搜索树的性质。但这是不是说我们随便挪到右儿子子树上的一个点的左儿子上就行了呢?显然不可以,因为那样不能保证右儿子整体还满足二叉搜索树的性质。
????????我们知道这样一件事,右儿子子树的最小值在哪呢?根据性质很简单,在右儿子子树的最左节点上。如果不在最左节点上,那显然不满足二叉搜索树的性质。我们将删除节点的左子树插入进去后,新的最小值应该是谁呢?很明显就是插进去的这些值。那我们就知道了,这颗左子树插进去要位于最左端。那最左端在哪呢?很明显,原本的最左节点的左儿子,就是最左端,我们将左子树插入到这个地方即可。
? ? ? ? 这样插入完成后,右子树依然满足二叉搜索树性质。并且整颗子树回到情况2,因为左子树为空,我们返回右儿子子树根节点即可。
? ? ? ? 总的来说,就是将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
核心代码:
/**
* 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;
if(root->val==key){
if(!root->left&&!root->right){
delete root;
return NULL;
}
else if(!root->left){
TreeNode* cur=root->right;
delete root;
return cur;
}
else if(!root->right){
TreeNode* cur=root->left;
delete root;
return cur;
}
else {
TreeNode* cur=root->right;
while(cur->left) cur=cur->left;
cur->left=root->left;
TreeNode* tmp=root;
root=root->right;
delete tmp;
return root;
}
}
if (root->val>key) root->left=deleteNode(root->left,key);
if (root->val<key) root->right=deleteNode(root->right,key);
return root;
}
};
????????今日学习时长3h,题目不算难,但我的解法依旧不算好,没来的及写好的,后面再回来认真总结这几天的题目。