(22/60)二叉搜索树的最近公共祖先、二叉搜索树的插入操作、删除二叉搜索树的节点
二叉搜索树的最近公共祖先
leetcode:235. 二叉搜索树的最近公共祖先
递归法
思路
搜索树节点是有序的,若节点值大于p、q则在右子树查询;若节点值小于p、q则在左子树查询;其他情况(节点值在两者之间、节点值等于p值或q值),node就是最近的公共祖先。
复杂度分析
时间复杂度:O(logN)。每次递归减少一半规模。
空间复杂度:递归栈深度。最好O(logN),最差O(N)。
注意点
- 为甚麽遇到的第一个就是最近公共祖先:cur在p和q之间,p、q分别在cur的两个子树上。向下一步,都会使得子树不再包含p、q。
代码实现
/**
* 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:
// 公共节点一定在p、q之间,递归遍历
TreeNode* ancestor;
void traversal(TreeNode* node,TreeNode* p,TreeNode* q){
if(!node) return;
if(node->val > p->val && node->val > q->val) traversal(node->left,p,q);
else if(node->val < p->val && node->val < q->val) traversal(node->right,p,q);
else ancestor = node; // 因为祖先一定存在,可以这样写
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
traversal(root,p,q);
return ancestor;
}
};
迭代法
思路
cur比两个都大,左移;cur比两个都小,右移;其他情况cur就是最近公共祖先。
复杂度分析
时间复杂度:O(logN)。
空间复杂度:O(1)。无额外辅助空间。
注意点
略
代码实现
/**
* 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) return NULL;
TreeNode* cur = root;
while(cur){
if(cur->val > p->val && cur->val > q->val) cur = cur->left;
else if(cur-> val < p->val && cur->val < q->val) cur = cur->right;
else return cur;
}
return NULL;
}
};
二叉搜索树的插入
leetcode:701. 二叉搜索树中的插入操作
递归法
思路
就记得一句——“对搜索树遍历是对搜索树的侮辱”。
-
节点为空时,返回构造的新节点。
-
val值小于节点值时,节点左子节点接收左子节点的递归;
val值大于节点值时,节点右子节点接收右子节点的递归。
-
(完成左右子树的构造后)最后返回root。
复杂度分析
时间复杂度:O(logN)。
空间复杂度:递归栈深度。最好O(logN),最差O(N)。
注意点
略
代码实现
/**
* 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) return new TreeNode(val); // 节点为空时,返回构造的新节点
if(val < root->val) root->left = insertIntoBST(root->left,val); // 接收节点
if(val > root->val) root->right = insertIntoBST(root->right,val);
return root; // root左右子树构造完成后返回root
}
};
迭代法
思路
见缝插针。根据题设,所有节点数值不重复,所以一定能找到位置插入。
只要根据搜索树规则,移动到指定位置,且该位置空着,插入新节点即可;否则,向下移动继续循环。
复杂度分析
时间复杂度:O(logN)。和二叉搜索树的查找一样。
空间复杂度:O(1)。无额外空间。
注意点
略
代码实现
/**
* 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) return new TreeNode(val);
TreeNode* cur = root;
while(cur){ // cur不为空
if(val < cur->val){ // val小于cur的值
if(cur->left) cur = cur->left; // 如果cur->left被占了,cur继续向下移动
else{ // 否则,cur->left就为构造新node,跳出循环。
cur->left = new TreeNode(val);
break;
}
}else if(val > cur->val){
if(cur->right) cur = cur->right;
else{
cur->right = new TreeNode(val);
break;
}
}
}
return root;
}
};
删除二叉搜索树中的节点
leetcode:450. 删除二叉搜索树中的节点
思路
找到key和没找到key两大类,总共五种情况。其中2、3、4、5是左、右节点空、非空状态的排列组合的四种情况。
-
没找到,向上返回NULL。
-
是叶子节点(左右都空),删除(释放)节点,向上返回NULL。
-
左不空右空,删除节点,向上返回左子树。
-
左空右不空,删除节点,向上返回右子树。
-
左右都不空,找一棵树为主另一颗为副,先进行副子树依附主子树的转移,再按照3或4中的情况处理。
以右子树为主为例,左子树需要依附到一个比root大但最小的子节点上:
- 找到右子树的最左子节点;
- 令右子树的最左子节点链接上左子树
root->left; - 删除节点;
- 向上返回右子树。
复杂度分析
时间复杂度:O(logN)。搜索树,懂得都懂。
空间复杂度:递归栈深度。平衡时O(logN),极倾斜时O(N)。
注意点
略
代码实现
/**
* 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:
// 五种情况,
// 其中2、3、4、5是左、右节点空、非空状态的排列组合的四种情况
// 1. 没找到
// 2. 是叶子节点
// 3. 左不空右空
// 4. 左空右不空
// 5. 左右都不空
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root) return NULL;
if(root->val == key){
if(!root->left && !root->right){
delete root;
return NULL;
}else if(root->left && !root->right){
TreeNode* leftNode = root->left;
delete root;
return leftNode;
}else if(!root->left && root->right){
TreeNode* rightNode = root->right;
delete root;
return rightNode;
}else{ // 以右子树为主
TreeNode* cur = root->right;
while(cur->left){ // 找右节点中最左边的子节点(比root大的最小节点)
cur = cur->left;
}
cur->left = root->left;
cur = root->right; // cur指向root的右子树
delete root;
return cur; // 返回原root右子树
}
}
// 搜索树进行下去
if(key < root->val) root->left = deleteNode(root->left,key);
if(key > root->val) root->right = deleteNode(root->right,key);
return root;
}
};
以左子树为主的版本:
// ...
else{ // 试试以左子树为主
TreeNode* cur = root->left;
while(cur->right) cur = cur->right; // 在左树中找比root小值里的最大值,将root的右子树挂在上面
cur->right = root->right; // 把root右子树挂在左子树的最大节点上
cur = root->left;
delete root;
return cur;
}
// ...

浙公网安备 33010602011771号