算法训练营 day22 二叉树 二叉搜索树的最近公共祖先 二叉搜索树中的插入操作 删除二叉搜索树中的节点
二叉搜索树的最近公共祖先
235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
因为是有序树,所有 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。
递归法
-
确定递归函数返回值以及参数
参数就是当前节点,以及两个结点 p、q。
返回值是要返回最近公共祖先,所以是TreeNode 。
-
确定终止条件
遇到空返回就可以了
-
确定单层递归的逻辑
在遍历二叉搜索树的时候就是寻找区间[p.val, q.val](注意这里是左闭又闭)
那么如果 cur.val 大于 p.val,同时 cur.val 大于q.val,那么就应该向左遍历(说明目标区间在左子树上)。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) return null;
if (root.val > p.val && root.val > q.val) {
TreeNode left = lowestCommonAncestor(root.left, p, q);
if (left!=null) return left;
}
if (root.val < p.val && root.val < q.val) {
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (right!=null) return right;
}
return root;
}
}
//精简
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left,p,q);
if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right,p,q);
return root;
}
}
迭代法
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while (true){
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. 二叉搜索树中的插入操作 - 力扣(LeetCode)
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
如下演示视频中可以看出:只要按照二叉搜索树的规则去遍历,遇到空节点就插入节点就可以了。
递归法
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) // 如果当前节点为空,也就意味着val找到了合适的位置,此时创建节点直接返回。
return new TreeNode(val);
if (root.val < val){
root.right = insertIntoBST(root.right, val); // 递归创建右子树
}else if (root.val > val){
root.left = insertIntoBST(root.left, val); // 递归创建左子树
}
return root;
}
}
迭代法
class Solution {
public static TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) return new TreeNode(val);
TreeNode result = root;
TreeNode pre=null;
while (root != null) {
pre = root;//保存前一个节点
if (root.val < val) root = root.right;
else if (root.val > val) root = root.left;
}
if (root==null){
if (pre.val > val) {
pre.left = new TreeNode(val);
} else {
pre.right = new TreeNode(val);
}
}
return result;
}
}
删除二叉搜索树中的节点
450. 删除二叉搜索树中的节点 - 力扣(LeetCode)
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
这里就把二叉搜索树中删除节点遇到的情况都搞清楚。
有以下五种情况:
- 第一种情况:没找到删除的节点,遍历到空节点直接返回了
- 找到删除的节点
- 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
- 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
- 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
- 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
递归法
class Solution {
public TreeNode deleteNode(TreeNode root, int 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.right!=null&&root.right==null){
return root.right;
}
//第五种情况目标值左右都不为空
else {
TreeNode cur = root.right;
while (cur.left!=null) cur = cur.left;
cur.left = root.left;
return root.right;
}
}
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 deleteNode(TreeNode root, int key) {
if (root == null) return null;
TreeNode cur = root;
TreeNode pre = null;
while (cur != null) {
if (cur.val == key) break;
pre = cur;
if (cur.val > key) cur = cur.left;
else if (cur.val < key) cur = cur.right;
}
if (pre == null) {
return deleteOneNode(cur);
}
if (pre.left != null && pre.left.val == key) {
pre.left = deleteOneNode(cur);
}
if (pre.right != null && pre.right.val == key) {
pre.right = deleteOneNode(cur);
}
return root;
}
private TreeNode deleteOneNode(TreeNode root) {
//第二种情况目标值为叶子节点
if (root.left==null&&root.right==null){
return null;
}
//第三种情况目标值左为空 右不为空
else if (root.left!=null&&root.right==null){
return root.left;
}
//第四种情况目标值右为空 左不为空
else if (root.right!=null&&root.right==null){
return root.right;
}
//第五种情况目标值左右都不为空
else {
TreeNode cur = root.right;
while (cur.left!=null) cur = cur.left;
cur.left = root.left;
return root.right;
}
}
}