一个人的朝圣 — LeetCode打卡第22天
知识总结
今天是二叉搜索树专题, 尤其是二叉搜索树的插入和删除, 需要掌握
Leetcode 235. 二叉搜索树的最近公共祖先
题目说明
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
代码说明
方法一: 按照一般二叉树的方法
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return dfs(root, p, q);
}
public TreeNode dfs(TreeNode root, TreeNode p, TreeNode q){
if (root == null) return null;
if (root == p || root == q) return root;
TreeNode left = dfs(root.left, p, q);
TreeNode right = dfs(root.right, p, q);
if(left != null && right != null) return root;
if(left == null && right != null) return right;
if(left != null && right == null) return left;
return null;
}
}
方法二, 利用二叉搜索树的特性, 一边大一边小说明需要该root为最小的公共祖先, 所以我们使用异或的操作
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return dfs(root, p, q);
}
public TreeNode dfs(TreeNode root, TreeNode p, TreeNode q){
if (root == null || root.val == p.val || root.val == q.val) return root;
if((root.val - p.val > 0) ^ (root.val - q.val > 0)) return root;
if((root.val - p.val) > 0 && (root.val - q.val) > 0) {
return dfs(root.left, p, q);
}else{
return dfs(root.right, p, q);
}
}
}
方法三, 迭代法, 比较好理解,从上往下依次遍历
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
TreeNode ancestor = root;
while(true){
if(ancestor.val > p.val && ancestor.val > q.val){
ancestor = ancestor.left;
}else if(ancestor.val < p.val && ancestor.val < q.val){
ancestor = ancestor.right;
}else{
break;
}
}
return ancestor;
}
}
Leetcode 701. 二叉搜索树中的插入操作
题目说明
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
代码说明
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null) return new TreeNode(val);
dfs(root, val);
return root;
}
public void dfs(TreeNode root, int val){
if(root.val > val){
if(root.left == null){
root.left = new TreeNode(val);
}else{
dfs(root.left, val);
}
}else{
if(root.right == null){
root.right = new TreeNode(val);
}else{
dfs(root.right, val);
}
}
}
}
Leetcode 450. 删除二叉搜索树中的节点
题目说明
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
代码说明
方法一: 递归法, 分类讨论, 当需要被删除的节点两边都要子树时, 需要找一个继承者successor(右子树的最小节点)来取代删除的节点。
时间复杂度:
O
(
N
)
O(N)
O(N)
空间复杂度:
O
(
N
)
O(N)
O(N)
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null) return root;
if(root.val > key) {
root.left = deleteNode(root.left, key);
return root;
}
if(root.val < key) {
root.right = deleteNode(root.right, key);
return root;
}
// 相等
// 叶子节点
if(root.left == null && root.right == null){
return null;
}else if(root.left == null && root.right != null){
return root.right;
}else if(root.left != null && root.right == null){
return root.left;
}else{
// 两边都有, 找到继承者
TreeNode successor = root.right;
while(successor.left != null){
successor = successor.left;
}
root = deleteNode(root, successor.val);
successor.left = root.left;
successor.right = root.right;
return successor;
}
}
方法二: 不去递归, 储存一个额外的父节点来迭代, 代码相对复杂不少。
时间复杂度
O
(
N
)
O(N)
O(N)
空间复杂度
O
(
1
)
O(1)
O(1)
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
TreeNode cur = root, curParent = null;
while(cur != null && cur.val != key){
curParent = cur;
cur = cur.val > key ? cur.left : cur.right;
}
if(cur == null) return root;
if(cur.left == null && cur.right == null){
cur = null;
}else if(cur.right == null){
cur = cur.left;
}else if(cur.left == null){
cur = cur.right;
}else{
TreeNode successor = cur.right, succPar = cur;
while(successor.left != null){
succPar = successor;
successor = successor.left;
}
// 删除successors
if(succPar.val == cur.val){
succPar.right = successor.right;
}else{
succPar.left = successor.right;
}
// successor 取代原节点
successor.right = cur.right;
successor.left = cur.left;
cur = successor;
}
if(curParent == null){
return cur;
}else{
if(curParent.left != null && curParent.left.val == key){
curParent.left = cur;
}else{
curParent.right = cur;
}
return root;
}
}
}