题目链接:235 二叉搜索树的最近公共祖先
题干: 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉搜索树中。
思考一:递归法。终止条件:如果当前节点为空,则说明查找失败,返回空。如果当前节点val值在两目标节点val值之间则说明查找成功,返回当前节点。(第二个的终止条件不用写,理由在后面) 单层递归逻辑:若当前节点val值均大于两目标节点val值则递归处理左子树,若当前节点val值均小于两目标节点val值则递归处理右子树。剩下的情况刚好是在中间的情况,直接返回当前节点即可。故第二个终止条件不用写
代码:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root) return nullptr; //查找到空节点则查找失败
if (root->val > p->val && root->val > q->val) //当前节点比目标节点元素值大
return lowestCommonAncestor(root->left, p, q);
else if (root->val < p->val && root->val < q->val) //当前节点比目标节点元素值小
return lowestCommonAncestor(root->right, p, q);
else return root; //查找成功
}
};
思考二: 迭代法。按递归法修改二叉搜索树搜索的迭代法的处理逻辑即可
二叉搜索树搜索的迭代法:第二十天| 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树
代码:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while (root) {
if (root->val > p->val && root->val > q->val)
root = root->left;
else if (root->val < p->val && root->val < q->val)
root = root->right;
else break;
}
return root;
}
};
题目链接:701 二叉搜索树中的插入操作
题干: 给定二叉搜索树(BST)的根节点
root
和要插入树中的值value
,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
- 可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
- 给定的树上的节点数介于 0 和 10^4 之间
- 每个节点都有一个唯一整数值,取值范围从 0 到 10^8
- -10^8 <= val <= 10^8
- 新值和原始二叉搜索树中的任意节点值都不同
思考一:递归法-无返回值。按节点有序性考虑,找到匹配位置的叶子节点,val值比较后修改当前节点的孩子节点
代码:
class Solution {
public:
void traversal(TreeNode* node, int val) {
if (node->val > val){
if (node->left)
traversal(node->left, val);
else
node->left = new TreeNode(val);
}
if (node->val < val){
if (node->right)
traversal(node->right, val);
else
node->right = new TreeNode(val);
}
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (!root) root = new TreeNode(val);
traversal(root, val);
return root;
}
};
思考二:递归法-无返回值。设置全局变量记录前一个结点作为最后插入的父结点,按节点有序性考虑,找到匹配位置的空节点,val值比较后修改父节点的孩子节点
代码:
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;
}
};
思考三:递归法-有返回值。按节点有序性考虑,终止条件:查找到空节点创建新节点返回。单层递归逻辑:val值比较后修改每个当前节点左右孩子节点。
代码:
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (!root) { //插入
TreeNode* node = new TreeNode(val);
return node;
}
if (root->val > val) root->left = insertIntoBST(root->left, val);
if (root->val < val) root->right = insertIntoBST(root->right, val);
return root;
}
};
思考四:迭代法。按递归法修改二叉搜索树搜索的迭代法的处理逻辑即可,查询并记录父节点,比较val值后插入。
二叉搜索树搜索的迭代法:第二十天| 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树
代码:
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
TreeNode* node = new TreeNode(val);
if (!root) return node;
TreeNode* parent = root; //记录前一个节点
TreeNode* cur = root;
while (cur) {
parent = cur;
if(cur->val > val) cur = cur->left;
else cur = cur->right;
}
if (parent->val > val) parent->left = node;
else parent->right = node;
return root;
}
};
题干链接:450 删除二叉搜索树中的节点
题干:给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
- 节点值唯一
root
是合法的二叉搜索树
思考一:递归法。终止条件:遇到空节点则说明要删除的节点在二叉树中未查询到。遇到要删除的节点,进行删除逻辑(五种情况分别处理)。单层递归逻辑:比较val值判断递归处理左孩子节点还是右孩子节点。
二叉搜索树中删除节点遇到的情况:
代码:
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return nullptr; //情况一:不存在目标节点
if (root->val == key) {
if (!root->left && !root->right) { //情况二:左右孩子均不存在
delete root;
return nullptr;
} else if (!root->left) { //情况三:左孩子不存在,右孩子存在
auto right = root->right;
delete root;
return right;
} else if (!root->right) { //情况四:左孩子存在,右孩子不存在
auto left = root->left;
delete root;
return left;
} 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;
}
};
思考二:迭代法。先寻找目标节点及其父节点,再按上法删除节点处理逻辑处理即可。
代码:
class Solution {
public:
TreeNode* deleteTarget(TreeNode* node) { //删除二叉搜索树的目标节点
if (!node) return nullptr; //情况二
if (!node->left) return node->right; //情况三
if (!node->right) return node->left; //情况四
//情况五
TreeNode* cur = node->right;
while (cur->left)
cur = cur->left;
cur->left = node->left;
return node->right;
}
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return nullptr;
TreeNode* cur = root; //记录
TreeNode* pre = nullptr; //记录父节点
while (cur) { //寻找目标节点即其父节点
if (cur->val == key) break;
pre = cur;
if (cur->val > key) cur = cur->left;
else cur = cur->right;
}
if (!pre) //二叉搜索树只要一个节点
return deleteTarget(cur);
//确定要删除的是左孩子还是右孩子
if (pre->left && pre->left->val == key)
pre->left = deleteTarget(cur);
if (pre->right && pre->right->val == key)
pre->right = deleteTarget(cur);
return root;
}
};
自我总结: