235.二叉搜索树的最近公共祖先
难度指数:😀😐
Q:此时节点5是不是最近公共祖先?
A:如果 从节点5继续向左遍历,那么将错过成为q的祖先, 如果从节点5继续向右遍历则错过成为p的祖先。
所以当我们从上向下去递归遍历,第一次遇到 cur节点是数值在[p, q]区间中,那么cur就是 p和q的最近公共祖先。
理解这一点,本题就很好解了。
递归解法:
代码思路:
3️⃣ 单层递归的逻辑:
在这道题,不用涉及到前中后序,因为二叉搜索树本身就是有序的,而且不需要对中间节点进行处理;
只需要有一个左和一个右就可以了,中在哪里无所谓。
TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {
if (cur == NULL) { //刚传进来的根节点为空
return NULL;
}
//左
if (cur->val > p->val && cur->val > q->val) { //当前节点cur比p和q大,说明应该向左子树去搜索
TreeNode* left = traversal(cur->left, p, q);
if (left != NULL) { //left不为空
return left; //就找到了最近的公共祖先
}
}
//右
if (cur->val < p->val && cur->val < q->val) {
TreeNode* right = traversal(cur->right, p, q);
if (right != NULL) {
return right;
}
}
return cur;
}
AC代码: (核心代码模式)
/**
* 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* cur, TreeNode* p, TreeNode* q) {
if (cur == NULL) {
return NULL;
}
//左
if (cur->val > p->val && cur->val > q->val) {
TreeNode* left = lowestCommonAncestor(cur->left, p, q);
if (left != NULL) { //若left不为空
return left; //就找到了最近的公共祖先
}
}
//右
if (cur->val < p->val && cur->val < q->val) {
TreeNode* right = lowestCommonAncestor(cur->right, p, q);
if (right != NULL) {
return right;
}
}
return cur;
}
};
迭代解法:
这道题的迭代法也很简单,就因为它是一棵二叉搜索树。
在二叉搜索树中,它把我们搜索的方向确定了,这就造成了本题的代码很简单。
701.二叉搜索树中的插入操作
难度指数:😀😐😕
在二叉搜索树中插入任何一个节点,都可以在叶子节点找到它的位置。
有人会问:为什么就只在叶子节点处插入?为什么不往中间其他位置插入呢?
这样改变二叉树的结构就做复杂了。
因此,要想新插入节点,我们只需考虑在叶子节点处插入就可以了。
代码思路:
1️⃣ 确定递归函数的参数和返回值:
- 传入的参数:这棵二叉树的根节点、我们要插入的数数值val
- 返回值:插入新的节点后,新的二叉搜索树的根节点
2️⃣ 终止条件:在本题中,遇到终止条件的时候,就找到了要插入节点的位置。
TreeNode* insert(root, val) {
if (root == NULL) {
TreeNode* node = new TreeNode(val);
return node;
}
if (val < root->val) {
root->left = insert(root->left, val);
}
if (val > root->val) {
root->right = insert(root->right, val);
}
return root;
}
450.删除二叉搜索树中的节点
难度指数:😀😐😕😖
本题删除节点比上一题插入节点要难很多
删除节点之后要保证这棵二叉树依然是二叉搜索树,也就意味着不可避免地会改变这棵二叉搜索树的结构。
在二叉搜索树中删除节点,有以下5种情况:
1️⃣ 没找到要删除的点
(下面这些情况都是可以找到要删除的点)
2️⃣ 要删的点是叶子节点(左为空,右为空) (最好删的,因为无需改变二叉搜索树的结构)
3️⃣ 要删的节点,左不空,右为空;让其父节点直接指向其左孩子
4️⃣ 要删的节点,左为空,右不空;让其父节点直接指向其右孩子
5️⃣ 要删的节点,左不空,右不空;(最复杂,因为需要大幅调整二叉搜索树的结构)
第五种情况有点难以理解,看下面动画:
让 7 的左、右孩子,谁去继位都可以,策略都是相似的,这里是让 7 的右孩子继位。
(让左子树继位是什么样子?建议画图理解,不要眼高手低)
这道题删除复杂,代码也有点复杂
代码思路:
确定递归函数的返回值和参数:
传入这棵二叉树的根节点 root ,要删的节点 key ;删除节点后,返回新的二叉树的根节点
终止条件:
本题的终止条件相对比较复杂。
因为在这棵二叉搜索树中,找到目标节点就要进行删除操作,删除节点的操作就在终止条件里。
第 5️⃣ 步:要删掉节点 7 ,应该找到节点 7 右子树中最左侧的值,因为最左侧的值是仅次于 7 大的数
TreeNode* delete(root, key) {
//终止条件 (比较复杂)
if (root == NULL) { //没有找到要删除的节点
return NULL;
}
if (root->val == key) { //找到要删除的节点
if (root->left == NULL && root->right == NULL) { //左为空,右为空
return NULL;
}
else if (root->left != NULL && root->right == NULL) { //左不为空,右为空
return root->left; //将该节点的左子树向上一层返回 (该节点就被移除了)
}
else if (root->left == NULL && root->right != NULL) { //左为空,右不为空
return root->right;
}
else { //左不空,右不空 (最复杂)
cur = root->right;
while (cur->left != NULL) {
cur = cur->left;
}
cur->left = root->left;
return root->right;
}
}
//单层递归的逻辑
if (key < root->val) {
root->left = delete(root->left, key);
}
if (key > root->val) {
root->right = delete(root->right, key);
}
return root;
}
严格来说,这里删除的节点都是从二叉树中移除了,这里并没有写在内存中真正释放(C++需要写)。
AC代码: (核心代码模式)
/**
* 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 == NULL) { //没有找到要删除的节点
return root;
}
if (root->val == key) { //找到要删除的节点
if (root->left == NULL && root->right == NULL) { //左为空,右为空
delete root;
return NULL;
}
else if (root->right == NULL) {
auto retNode = root->left;
delete root;
return retNode;
}
else if (root->left == NULL) {
auto retNode = root->right;
delete root;
return retNode;
}
else { //左不空,右不空 (比较复杂)
TreeNode* cur = root->right;
while (cur->left != NULL) {
cur = cur->left;
}
cur->left = root->left;
TreeNode* tmp = root;
root = root->right;
delete tmp;
return root;
}
}
//单层递归的逻辑
if (key < root->val) {
root->left = deleteNode(root->left, key);
}
if (key > root->val) {
root->right = deleteNode(root->right, key);
}
return root;
}
};