①、二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
事例:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 输出: 6 解释: 节点 2 和结点 8 的最近公共祖先为结点 6。
思路:
普通二叉树的最近公共祖先需要自底向上查询,即后序遍历先收集左右子树结果最终返回。二叉搜索树可以利用中序遍历结点值有序的特点,不难得出祖先都在两结点中间,即祖先的结点值介于两节点之间。故可以使用从上到下即前序遍历方法获得最近公共祖先。
Ps:当递归结点第一次介于两结点之间时,两结点一个位于左子树一个位于右子树,此时应当返回结果。此时若往左或往右走,都会错过其中一个结点。
代码:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//递归
//return traversal(root,p,q);
//迭代
TreeNode cur = root;
while(cur != null){
if(p.val < cur.val && q.val < cur.val) cur = cur.left;
else if(p.val > cur.val && q.val > cur.val) cur = cur.right;
else return cur;
}
return null;
}
public TreeNode traversal(TreeNode root,TreeNode p,TreeNode q){
if(root == null) return root;
if(p.val < root.val && q.val < root.val){
TreeNode left = traversal(root.left,p,q);
if(left != null) return left;
}
if(p.val > root.val && q.val > root.val){
TreeNode right = traversal(root.right,p,q);
if(right != null) return right;
}
return root;
}
②、二叉搜索树中的插入操作
给定二叉搜索树(BST)的根节点
root
和要插入树中的值value
,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
事例:
输入:root = [4,2,7,1,3], val = 5 输出:[4,2,7,1,3,5]
思路:
插入途中要保存二叉搜索树的性质不变,即中序遍历结点值是有序的。故可以根据目标值判断当前结点向左或向右走。即遍历树,使用全局变量pre记录前一个结点,当遍历到空节点时,pre插入目标值的结点即可。
代码:
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null) return new TreeNode(val);
// 递归
// insertNode(root,val);
// return root;
//迭代
TreeNode cur = root;
TreeNode pre = null;
while(cur != null){
pre = cur;
if(cur.val < val){
cur = cur.right;
}else {
cur = cur.left;
}
}
TreeNode tmp = new TreeNode(val);
if(pre.val < val) pre.right = tmp;
if(pre.val > val) pre.left = tmp;
return root;
}
private TreeNode pre = null;
public void insertNode(TreeNode root,int val){
if(root == null){
TreeNode tmp = new TreeNode(val);
if(pre.val < val) pre.right = tmp;
else pre.left = tmp;
return;
}
pre = root;
if(root.val < val){
//往右边寻找
insertNode(root.right,val);
}else{
//往左边寻找
insertNode(root.left,val);
}
}
③、删除二叉搜索树中的结点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
事例:
输入:root = [5,3,6,2,4,null,7], key = 3 输出:[5,4,6,2,null,null,7]
思路:
跟上题一样,利用二叉搜索树的特性,当前值比目标值大时往左走,比目标值小时往右走,当两值相等时,进行删除操作。可以利用递归返回值结果(当往左走时,返回的是删除操作完后的左子树),故root.left = delete(root.left,key) root.right = delete(root.right,key),最后返回完整的树即为最终结果。
具体删除操作:
当目标结点为叶子结点时,返回null
当目标结点只有左子树或右子树时,返回不为null的子树
当目标结点既有左子树又有右子树时,在右子树中找到最小值(即右子树中的最左结点),将最小值赋给目标结点,此时,右子树中的最左结点就重复了,变成了删除右子树中的最左结点,root.right = delete(root.right,root.val)。
代码:
public TreeNode deleteNode(TreeNode root, int key) {
root = delete(root,key);
return root;
}
public TreeNode delete(TreeNode root,int key){
if(root == null) return null;
if(key < root.val){
//往左边寻找删除
root.left = delete(root.left,key);
}else if(key > root.val){
//往右边寻找删除
root.right = delete(root.right,key);
}else{
//删除目标结点
if(root.left == null) return root.right;
else if(root.right == null) return root.left;
else{
//目标结点既有左子树又有右子树
TreeNode tmp = root.right;
//寻找右子树中的最小值
while(tmp.left != null){
tmp = tmp.left;
}
//交换比标值和右子树中的最小值
root.val = tmp.val;
//删除赋值过来的结点
root.right = delete(root.right,tmp.val);
}
}
return root;
}