目录
- 一、LeetCode 235. 二叉搜索树的最近公共祖先
- 思路:
- C++代码
- 二、LeetCode 701.二叉搜索树中的插入操作
- 思路
- C++代码
- 三、LeetCode 450.删除二叉搜索树中的节点
- 思路
- C++代码
- 总结
一、LeetCode 235. 二叉搜索树的最近公共祖先
题目链接:LeetCode 235. 二叉搜索树的最近公共祖先
文章讲解:代码随想录
视频讲解:自底向上查找,有点难度! | LeetCode:236. 二叉树的最近公共祖先
思路:
判断两个结点在二叉树中的最近公共祖先,笔者的算法思路为设置全局变量ances
记录最近的公共祖先结点,设计递归函数searchOffspring
自底向上地遍历结点,递归函数返回bool
值来判断子树中是否含有p
或q
。
递归函数主体中,判断当前结点是否是最近祖先有两种情况:
- 当前结点的左右子树分别出现过
p
和q
; - 当前结点为
p
或q
中的一个,另一个在当前结点的子树中
递归终止条件为遍历到空节点,此时返回false
;
C++代码
/**
* 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* ances; //全局变量记录最近祖先结点
bool searchOffspring(TreeNode* node, TreeNode* p, TreeNode* q){
//使用bool值记录当前结点的子树是否出现过p或q
if(!node) return false;
bool left = searchOffspring(node->left, p, q);
bool right = searchOffspring(node->right, p, q);
if(node == p || node == q){ //若当前结点和p、q相同
if(left || right){
ances = node;
return false; //防止最近祖先上方结点重复赋值
}
return true;
}
else if(left && right){ //当前结点的左右子树出现过p、q,则为最近祖先。
ances = node;
return false;
}
return left || right;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
searchOffspring(root, p, q);
return ances;
}
};
二、LeetCode 701.二叉搜索树中的插入操作
题目链接:LeetCode 701.二叉搜索树中的插入操作
文章讲解:代码随想录
视频讲解:原来这么简单? | LeetCode:701.二叉搜索树中的插入操作
思路
给二叉搜索树中插入新元素,由于题目没有要求二叉搜索树平衡的条件,那么只需要按照二叉搜索的方式递归遍历,直到遍历到第一个空的孩子结点,把插入的新元素放到这个孩子结点的位置即可。
C++代码
/**
* 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) {
TreeNode* subroot = root;
while(subroot){
if(val < subroot->val){
if(subroot->left){
subroot = subroot->left;
continue;
}
else{
TreeNode* node = new TreeNode(val);
subroot->left = node;
return root;
}
}
else{
if(subroot->right){
subroot = subroot->right;
continue;
}
else{
TreeNode* node = new TreeNode(val);
subroot->right = node;
return root;
}
}
}
TreeNode* node = new TreeNode(val);
return node;
}
};
三、LeetCode 450.删除二叉搜索树中的节点
题目链接:LeetCode 450.删除二叉搜索树中的节点
文章讲解:代码随想录
视频讲解:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点
思路
本题也需要实现二叉搜索树的一个基本维护操作,但与插入操作不同的是,删除结点一般都是非叶子结点,意味着不能只单纯实现删除一个结点操作,还需要调整删除结点后,该结点左右孩子的结构,并连接到原来的二叉树上。
我们以二叉搜索树中一个含有左右孩子的一般结点为例。
首先,删除结点后,该结点的左右孩子结构如何调整?我们注意到,结点的左子树是包含所有小于该结点的数,右子树包含所有大于该结点的树,那么左子树的所有元素一定比右子树的最小元素小,借助这个特性,我们可以使用如下策略调整结构,即:
把左子树的根结点,连接到右子树的最左下方结点(右子树中的最小值)的左孩子位置。
调整好结构以后,需要考虑连接到原二叉树的问题。由于删除结点无论是他的双亲的左孩子还是右孩子,该结点的子树和该结点的双亲大小关系一定是确定的,所以修改好结构的子树,只需要把子树根结点(上一段右孩子的根节点)连接到双亲即可。
需要考虑几种特殊情况,当删除结点只有一个孩子结点时,直接连接该孩子结点即可,无需进行结构调整;当删除结点是叶子结点时,只删除结点即可。
C++代码
/**
* 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;
TreeNode* node; //记录需要删除的结点
TreeNode* subroot = root; //记录删除结点的双亲,便于调整结构
if(key == root->val){
if(!root->left){
return root->right;
}
if(!root->right){
return root->left;
}
node = root;
}
else{
node = (key < root->val) ? root->left: root->right;
while(node){//查找结点
if(key < node->val){
subroot = node;
node = node->left;
}
else if(key > node->val){
subroot = node;
node = node->right;
}
else break;
}
}
if(node){ //删除结点
TreeNode* inst;
if(node->right){
inst = node->right;
while(inst->left){
//把删除结点的左子树,放在删除结点右子树的最左边叶子位置
inst = inst->left;
}
inst->left = node->left;
inst = node->right;
}
else{
inst = node->left;
}
if(node->val < subroot->val) subroot->left = inst;
else subroot->right = inst;
if(node == root) root = inst;
}
return root;
}
};
总结
对于二叉搜索树中的删除结点操作应加强复习。
文章图片来源:代码随想录 (https://programmercarl.com/)