235. 二叉搜索树的最近公共祖先
相对于 二叉树的最近公共祖先 本题就简单一些了,因为 可以利用二叉搜索树的特性。题目链接/文章讲解:代码随想录
视频讲解:二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先_哔哩哔哩_bilibili
重点:
1. 二叉搜索树的最近公共祖先一定是第一个遇到的在p和q中间的值,不可能是第二个。因为如果遇到了第一个再接着向左遍历将错过在右边的值,往右遍历将错过在左边的值。
2. 二叉搜索树自带了搜索方向。根据值的大小比较可以判断出遍历方向
思路:
递归法:
1. 确定参数及返回值
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
2. 确定终止条件
if (root == null) { return null; }
3. 确定单层递归的逻辑
if (root.val > p.val && root.val > q.val) {} if (root.val < p.val && root.val < q.val) {}
迭代法:
1. 因为是二叉搜索树,根据值的大小我们就可以确定搜索方向,直接while循环即可
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 left = lowestCommonAncestor(root.left, p, q);
if (left != null) {
return left;
}
}
// 当前节点的值比p和q都小,往右走增大
if (root.val < p.val && root.val < q.val) {
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (right != null) {
return right;
}
}
// 当前节点的值值刚刚好在中间的情况
return root;
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 终止条件
if (root == null) {
return null;
}
while (root != null) {
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 {
// 当前节点的值正好在中间,说明找到了公共最近祖先
return root;
}
}
return null;
}
701. 二叉搜索树中的插入操作
本题比想象中的简单,大家可以先自己想一想应该怎么做,然后看视频讲解,就发现 本题为什么比较简单了。
题目链接/文章讲解:代码随想录
视频讲解:原来这么简单? | LeetCode:701.二叉搜索树中的插入操作_哔哩哔哩_bilibili
重点:
1. 二叉搜索树根据值就能判断出左右方向
2. 遇到叶子节点了就说明找到位置了
思路:
递归法:
1. 确定参数及返回值:
不用遍历全部树,只要找到对应位置即可,所以需要返回值
public TreeNode insertIntoBST(TreeNode root, int val)
2. 确定终止条件
遇到叶子节点说明找到位置了
if (root == null) { return new TreeNode(val); }
3. 确定单层递归的逻辑
当前节点的值小了,就往右走。当前节点的值大了,就往左走
if (root.val > val) { TreeNode left = insertIntoBST(root.left, val); root.left = left; } if (root.val < val) { TreeNode right = insertIntoBST(root.right, val); root.right = right; }
迭代法:
1. 双指针。用一个parent指针来保存上一个遍历的节点,这样方便后面找到位置后赋值。
public TreeNode insertIntoBST(TreeNode root, int val) {
// 终止条件
// 遇到叶子节点,找到位置了
if (root == null) {
return new TreeNode(val);
}
// 当前节点的值大了,往左走
if (root.val > val) {
TreeNode left = insertIntoBST(root.left, val);
// 回溯回来的时候正好就可以赋值给当前节点
root.left = left;
}
// 当前节点的值小了,往右走
if (root.val < val) {
TreeNode right = insertIntoBST(root.right, val);
// 回溯回来的时候正好就可以赋值给当前节点
root.right = right;
}
return root;
}
public TreeNode insertIntoBST(TreeNode root, int val) {
// 终止条件
// 遇到叶子节点,找到位置了
if (root == null) {
return new TreeNode(val);
}
TreeNode cur = root;
// parent记录上一个节点
TreeNode parent = null;
while (cur != null) {
parent = cur;
// 根据大小选择方向
if (cur.val > val) {
cur = cur.left;
} else {
cur = cur.right;
}
}
TreeNode node = new TreeNode(val);
// parent就是保存好的对应的父节点
if (val < parent.val) {
parent.left = node;
} else {
parent.right = node;
}
return root;
}
450. 删除二叉搜索树中的节点
相对于 插入操作,本题就有难度了,涉及到改树的结构题目链接/文章讲解:代码随想录
视频讲解:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点_哔哩哔哩_bilibili
重点:
1. 总共五种情况,没找到删除节点,删除节点为叶子节点,删除节点左不为空右为空,删除节点左为空右不为空,删除节点左右都不为空
思路:
递归法:
1. 确定参数及返回值
public TreeNode deleteNode(TreeNode root, int key)
2. 确定终止条件
没找到删除节点和找到了删除节点
删除节点分五种情况,分别讨论即可,注意左右都不为空的情况,需要先把要删除节点的左子树衔接给右子树的最小值当作左子树,然后再把要删除节点的右子树回溯给上一层递归
if (root == null) { return null; } // 找到要删除的节点 if (root.val == key) { // 叶子节点,直接return null就可以删除了 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 rightSmallest = root.right; while (rightSmallest.left != null) { rightSmallest = rightSmallest.left; } rightSmallest.left = root.left; // 衔接好root的左子树后,返回原来root的右子树即可 return root.right; } }
3. 确定单层递归的逻辑
if (root.val > key) { root.left = deleteNode(root.left, key); } if (root.val < key) { root.right = deleteNode(root.right, key); }
迭代法:
1. 双指针,一个指针为要删除的节点,一个指针为要删除节点的上一个节点,然后分五种情况讨论
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就可以删除了
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 rightSmallest = root.right;
while (rightSmallest.left != null) {
rightSmallest = rightSmallest.left;
}
rightSmallest.left = root.left;
// 衔接好root的左子树后,返回原来root的右子树即可
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;
}
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 {
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 target) {
if (target == null) {
return null;
}
// 叶子节点
if (target.left == null && target.right == null) {
return null;
}
// 左不为空,右为空
if (target.left != null && target.right == null) {
return target.left;
}
// 左为空,右不为空
if (target.left == null && target.right != null) {
return target.right;
}
// 左右都不为空
// 把要删除节点的左子树衔接好
TreeNode cur = target.right;
while (cur.left != null) {
cur = cur.left;
}
cur.left = target.left;
return target.right;
}