系列文章目录
目录
- 系列文章目录
- 235. 二叉搜索树的最近公共祖先
- ①递归法
- 自己写的
- 简洁版
- ②迭代法
- 不能这样写!
- 正确写法
- 701.二叉搜索树中的插入操作
- ①递归法
- ②迭代法
- 450.删除二叉搜索树中的节点
- 递归法
235. 二叉搜索树的最近公共祖先
①递归法
自己写的
class Solution {
TreeNode result;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//终止条件
if (root == null || root == p || root == q) return root;
if (root.val > p.val && root.val > q.val) {
result = lowestCommonAncestor(root.left,p,q);
} else if (root.val < p.val && root.val < q.val) {
result = lowestCommonAncestor(root.right,p,q);
} else {
return root;
}
return result;
}
}
简洁版
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;
}
}
②迭代法
不能这样写!
图片来自热心网友(大佬)
5
和6
的最近公共祖先是8
而不是4
。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while (root.val > p.val && root.val > q.val) root = root.left;
while (root.val < p.val && root.val < q.val) root = root.right;
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.二叉搜索树中的插入操作
①递归法
递归三部曲:
- 确定递归函数参数以及返回值:参数就是根节点指针,以及要插入元素,这里递归函数要不要有返回值呢?可以有,也可以没有,但递归函数如果没有返回值的话,实现是比较麻烦的,下面也会给出其具体实现代码。有返回值的话,可以利用返回值完成新加入的节点与其父节点的赋值操作。
- 确定终止条件:终止条件就是找到遍历的节点为
null
的时候,就是要插入节点的位置了,并把插入的节点返回。 - 确定单层递归的逻辑:
- 如果
root.val > val
,说明应该将val
插入左子树- 如果左子树不存在,将
val
作为root.left
- 如果左子树存在,让左子树插入
val
- 如果左子树不存在,将
- 如果
root.val < val
,说明应该将val
插在右子树- 如果右子树不存在,将
val
作为root.right
- 如果右子树存在,让右子树插入
val
- 如果右子树不存在,将
- 返回插入
val
后的根节点,即左右子树插入节点后的根节点。
- 如果
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
//终止条件
if (root == null) return new TreeNode(val);
if (root.val > val) {
root.left=insertIntoBST(root.left, val);
} else if (root.val < val) {
root.right=insertIntoBST(root.right, val);
}
return root;
}
}
②迭代法
迭代法遍历的过程中,需要记录一下当前遍历的节点的父节点,这样才能做插入节点的操作。用pre
指针来记录上一个节点。
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) return new TreeNode(val); // root为空,直接插入后返回
TreeNode newRoot = root;//记录根节点
TreeNode pre = null;// 这个很重要,需要记录上一个节点,否则无法赋值新节点
while (root != null) {
pre = root;
if (root.val > val) {
root = root.left;
} else if (root.val < val) {
root = root.right;
}
}
//插入节点的操作
if (pre.val > val) {
pre.left = new TreeNode(val);
} else {
pre.right = new TreeNode(val);
}
return newRoot;
}
}
450.删除二叉搜索树中的节点
二叉搜素树的性质:
左子树的所有val
值 <
当前节点的val
值 <
右子树的最小val
值 =
右子树的最左边节点的val
值。
递归法
递归三部曲:
- 确定递归函数参数以及返回值:通过递归返回值删除节点。
- 确定终止条件:删除当前节点的逻辑放在终止条件中,二叉搜索树中删除节点有以下五种情况:
- 第一种情况:没找到删除的节点,遍历到空节点直接返回了;
- 找到删除的节点
- 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点;
- 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点的右子树;
- 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点的左子树;
- 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点的右节点,作为删除后树的根节点。
注
:终止条件可以简化为:
if (root == null) return root;//找不到要删除的节点,返回null
if (root.val == key) {
if (root.left == null) {
return root.left;
} else if (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;
}
}
其中当要删除的节点的左右子树都不为空,一直循环找到右子树最左边的节点时,注意判断条件必须为while (cur.left != null)
而不能是while (cur != null)
,否则会在cur.left = root.left;
这里报空指针异常。
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
//终止条件
if (root == null) return root;//找不到要删除的节点,返回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 {//左右子树都不为空
TreeNode cur = root.right;
while (cur.left != null) {//一直循环找到右子树最左边的节点
cur = cur.left;
}
cur.left = root.left;//将要删除的节点的左子树拼接到右子树最左边的节点
return root.right;//返回要删除的节点右子树
}
}
// 如果root.val > key,证明要删除的节点应该会出现在左子树上,获取左子树删除key节点后的根节点作为新的左节点
if (root.val > key) root.left = deleteNode(root.left, key);
// 如果root.val < key,证明要删除的节点应该会出现在右子树上,获取右子树删除key节点后的根节点作为新的右节点
if (root.val < key) root.right = deleteNode(root.right, key);
// 返回删除key节点后的根节点
return root;
}
}