LeetCode # 235. 二叉搜索树的最近公共祖先
📖 考察点
二叉搜索树,从上到下便历,第一个遇到的在区间的节点就是最近公共祖先
📖 题意理解
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
💡 解题思路
从上到下便历。遇到第一个在区间内的节点返回
思路一:
思路二:
🔑 关键点总结
规律总结
💻 代码实现
JavaScript
var lowestCommonAncestor = function (root, p, 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 {
return root;
}
}
};
⏱️ 复杂度分析
📚 总结与反思
LeetCode 701.二叉搜索树中的插入操作
📖 考察点
二叉搜索树的性质
📖 题意理解
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
💡 解题思路
利用二叉搜索树的性质,在可以插入的位置如果没有节点,就插入
思路一:
思路二:
🔑 关键点总结
二叉搜索树的性质
💻 代码实现
JavaScript
var insertIntoBST = function (root, val) {
if (root) {
return new TreeNode(val);
}
let temp = root;
while (true) {
if (val > temp.val) {
if (temp.right) {
temp = temp.right;
} else {
temp.right = new TreeNode(val);
break;
}
}
if (val < temp.val) {
if (temp.left) {
temp = temp.left;
} else {
temp.left = new TreeNode(val);
break;
}
}
}
return root;
};
⏱️ 复杂度分析
📚 总结与反思
LeetCode 450.删除二叉搜索树中的节点
📖 考察点
二叉树的便历 平衡二叉树
📖 题意理解
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
💡 解题思路
删除节点,保持二叉搜索树的性质 分情况讨论,当找到key时 case1:没有子节点,直接返回null case2:只有单个节点,直接返回子节点 case3:子节点都不为空,此时为了保持二叉搜索树的性质,需要找到左子树的最大值或右子树的最小值,将当前节点的值设为该值,再递归调用函数删除子树中值为该值的元素。
思路一:
思路二:
🔑 关键点总结
二叉树的性质
💻 代码实现
JavaScript
// 1
var deleteNode = function (root, key) {
if (!root) {
return root;
}
// cur当前节点
let cur = root;
// 上一个节点
let head = new TreeNode(null);
let pre = head;
// 方向
let preDirection = "left";
pre.left = root;
while (true) {
if (key > cur.val) {
if (cur.right) {
pre = cur;
cur = cur.right;
} else {
// 不存在 直接结束循环
break;
}
} else if (key < cur.val) {
if (cur.left) {
pre = cur;
preDirection = "left";
cur = cur.left;
} else {
// 不存在,直接结束循环
break;
}
} else {
// 相等
if (!cur.left && !cur.right) {
pre[preDirection] = null;
} else if (!cur.left) {
pre[preDirection] = cur.right;
} else if (!cur.right) {
pre[preDirection] = cur.left;
} else {
// 都有。找左子树最大值 | 右子树最小值
if (!cur.left.right) {
cur.val = cur.left.val;
cur.left = deleteNode(cur.left, cur.left.val);
break;
}
cur = cur.left;
while (cur.right && cur.right.right) {
cur = cur.right;
}
pre[preDirection].val = cur.right.val;
cur.right = deleteNode(cur.right, cur.right.val);
}
break;
}
}
return head.left;
};
// 2
var deleteNode = function (root, key) {
if (!root) return null;
if (key > root.val) {
root.right = deleteNode(root.right, key);
return root;
} else if (key < root.val) {
root.left = deleteNode(root.left, key);
return root;
} else {
// case 1:节点为叶节点
if (!root.left && !root.right) {
return null;
}
// case 2:有一个孩子节点不存在
if (!root.left) {
return root.right;
}
if (!root.right) {
return root.left;
}
// case 3:左右节点都存在
const rightNode = root.right;
// 获取最小值节点
const minNode = getMinNode(rightNode);
// 将待删节点的值替换为最小值节点值
root.val = minNode.val;
root.right = deleteNode(root.right, minNode.val);
return root;
}
};
function getMinNode(root) {
while (root.left) {
root = root.left;
}
return root;
}