打卡第22天,平衡二叉树,难,难,难。
- 235.二叉搜索树的最近公共祖先
- 701.二叉搜索树中的插入操作
- 450.删除二叉搜索树中的节点
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
昨天的普通二叉树找最近公共祖先,直接搬过来了。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
// 递归出口
if(root == NULL) return NULL;
if(root == p || root == q) return root;
TreeNode* leftNode = lowestCommonAncestor(root->left, p, q); // 左
TreeNode* rightNode = lowestCommonAncestor(root->right, p, q); //右
// 中
if(leftNode != NULL && rightNode != NULL) return root; //左右节点返回均不为空,说明root是p、q最近公共祖先
if(leftNode != NULL && rightNode == NULL) return leftNode; //左节点不为空,说明左子树出现q或者q
if(leftNode == NULL && rightNode != NULL) return rightNode; //右节点不为空,说明右子树出现q或者q
return NULL; // 均为空,说明左右子树均不出现p、q
}
};
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == NULL) return NULL;
// 当p、q结点的值都 小于 此时结点的值,说明最近的公共祖先结点在左子树
if(root->val > p->val && root->val > q->val) {
TreeNode* leftNode = lowestCommonAncestor(root->left, p, q);
if(leftNode) return leftNode;
}
// 当p、q结点的值都 大于 此时结点的值,说明最近的公共祖先结点在右子树
if(root->val < p->val && root->val < q->val) {
TreeNode* rightNode = lowestCommonAncestor(root->right, p, q);
if(rightNode) return rightNode;
}
// 当p、q结点的值 不全都小于,也不全都大于 此时结点的值,说明p、q结点分别在此时结点的左右子树两侧,那么最近的公共祖先结点就是此时的结点
return root;
}
};
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while(root) {
// 当p、q结点的值都 小于 此时结点的值,说明最近的公共祖先结点在左子树
if(root->val < p->val && root->val < q->val) root = root->right;
// 当p、q结点的值都 大于 此时结点的值,说明最近的公共祖先结点在右子树
else if(root->val > p->val && root->val > q->val) root = root->left;
// 当p、q结点的值 不全都小于,也不全都大于 此时结点的值,说明p、q结点分别在此时结点的左右子树两侧,那么最近的公共祖先结点就是此时的结点
else break;
}
return root;
}
};
给定二叉搜索树(BST)的根节点
root
和要插入树中的值value
,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
按照二叉搜索树的规则去遍历,遇到空节点就插入节点就可以了
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
TreeNode *node = new TreeNode(val);
if(root == NULL) return node;
TreeNode *cur = root;
TreeNode* parent = root; //记录前一个结点
while(cur != NULL) {
parent = cur;
if(val < cur->val) cur = cur->left;
else cur = cur->right;
}
// 当遇到结点为空,插入我们的val结点
if(val > parent->val) {
parent->right = node;
}
else {
parent->left = node;
}
return root;
}
};
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root == NULL) {
TreeNode *node = new TreeNode(val);
return node;
}
if(val < root->val) root->left = insertIntoBST(root->left, val);
if(val > root->val) root->right = insertIntoBST(root->right, val);
return root;
}
};
递归函数不用返回值也可以,找到插入的节点位置,直接让其父节点指向插入节点,结束递归,也是可以的。
class Solution {
private:
TreeNode* parent;
void traversal(TreeNode* cur, int val) {
if (cur == NULL) {
TreeNode* node = new TreeNode(val);
if (val > parent->val) parent->right = node;
else parent->left = node;
return;
}
parent = cur;
if (cur->val > val) traversal(cur->left, val);
if (cur->val < val) traversal(cur->right, val);
return;
}
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
parent = new TreeNode(0);
if (root == NULL) {
root = new TreeNode(val);
}
traversal(root, val);
return root;
}
};
给定一个二叉搜索树的根节点
root
和一个值key
,删除二叉搜索树中的key
对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
// 第一种情况:没找到删除的节点,遍历到空节点直接返回 NULL
if(root == NULL) return NULL;
if(root->val == key) {
// 第二种情况:找到要删除的节点,而且他的左右节点都为空,直接返回 NULL
if(root->left == NULL && root->right == NULL) {
delete root; //释放内存
return NULL;
}
// 第三种情况:找到删除的节点,而且它的右节点为空,直接返回左孩子
else if(root->left != NULL && root->right == NULL) {
auto retNote = root->left;
delete root;
return retNote;
}
// 第四种情况:找到删除的节点,而且它的左节点为空,直接返回右孩子
else if(root->left == NULL && root->right != NULL) {
auto retNote = root->right;
delete root;
return retNote;
}
// 第五种情况:找到删除的节点,但是它的左右节点为空
// 找到该节点的右子树的最左节点,把该节点的左子树放在最左节点的左孩子
// 返回该节点的右孩子
else {
TreeNode* cur = root->right;
while(cur->left != NULL) cur = cur->left;
cur->left = root->left;
auto retNote = root;
root = root->right; //返回旧root的右孩子作为新root
delete retNote; //释放内存
return root;
}
}
if(root->val > key) root->left = deleteNode(root->left, key);
if(root->val < key) root->right = deleteNode(root->right, key);
return root;
}
};