目录
一、二叉树公共祖先问题
1、二叉树的最近公共祖先
祖先的定义: 若节点 pp 在节点 rootroot 的左(右)子树中,或 p = rootp=root ,则称 rootroot 是 pp 的祖先。
最近公共祖先的定义: 设节点 rootroot 为节点 p, qp,q 的某公共祖先,若其左子节点 root.leftroot.left 和右子节点 root.rightroot.right 都不是 p,qp,q 的公共祖先,则称 rootroot 是 “最近的公共祖先” 。
根据以上定义,若 rootroot 是 p, qp,q 的 最近公共祖先 ,则只可能为以下情况之一:
- pp 和 qq 在 rootroot 的子树中,且分列 rootroot 的 异侧(即分别在左、右子树中);
- p = rootp=root ,且 qq 在 rootroot 的左或右子树中;
- q = rootq=root ,且 pp 在 rootroot 的左或右子树中;
考虑通过递归对二叉树进行先序遍历,当遇到节点 pp 或 qq 时返回。从底至顶回溯,当节点 p, qp,q 在节点 rootroot 的异侧时,节点 rootroot 即为最近公共祖先,则向上返回 rootroot 。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null) return right;
if(right == null) return left;
return root;
}
}
2、二叉搜索树的最近公共祖先
- 二叉搜索树性质决定了:如果 p.val 和 q.val 都比 root.val 小,则p、q肯定在 root 的左子树。
- 那问题规模就变小了,递归左子树就行!
- 如果 p.val 和 q.val 都比 root.val 大,递归右子树就行!
- 其他情况,root 即为所求!那么简单吗?为什么?
- 只要 p.val 和 q.val 不是都大于(小于) root.val,即只要 p, q 不同处在 root 的一个子树
- 就只有这三种情况:
- p 和 q 分居 root 的左、右子树。
- root 就是 p,q 在 p 的子树中。
- root 就是 q,p 在 q 的子树中
- 而这三种情况,p 和 q 的最近公共祖先都是 root!是不是很简单!
class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if (root==null){ return null; } if (p.val<root.val&&q.val<root.val) return lowestCommonAncestor(root.left,p,q); if (p.val>root.val&&q.val>root.val) return lowestCommonAncestor(root.right,p,q); return root; } }
二、二叉搜索树的修改与改造
-
1、二叉搜索树中的插入操作
这道题很简单,只需要按照二叉搜索树的规则去遍历,遇到空结点直接插入就可以了
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root==null) return new TreeNode(val);//当我们遍历到空结点时,此结点的位置就是需要插入的位置
if (root.val>val){//root的val大于val,去左边寻找空结点
root.left=insertIntoBST(root.left,val);
}else {//小于,需要去右边寻找
root.right=insertIntoBST(root.right,val);
}
return root;
}
这道题还可以用非迭代的解法,但我们需要一个父亲结点,以便我们遍历到空结点时,把父亲的引用设置为新节点
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root==null){
return new TreeNode(val);
}
TreeNode cur=root;//定义一个cur结点去遍历树
TreeNode parent=root;//父亲节点
while (cur!=null){
parent=cur;
cur=cur.val>val?cur.left:cur.right;
}//退出循环说明cur一定是null
TreeNode node=new TreeNode(val);
if (parent.val>val){//node是左子树
parent.left=node;
}else {
parent.right=node;
}
return root;
}
2、删除二叉搜索树中的结点
根据二叉搜索树的性质
- 如果目标节点大于当前节点值,则去右子树中删除;
- 如果目标节点小于当前节点值,则去左子树中删除;
- 如果目标节点就是当前节点,分为以下三种情况:
- 其无左子:其右子顶替其位置,删除了该节点;
- 其无右子:其左子顶替其位置,删除了该节点;
- 其左右子节点都有:其左子树转移到其右子树的最左节点的左子树上,然后右子树顶替其位置,由此删除了该节点。
- 图片来源:代码随想录
public TreeNode deleteNode(TreeNode root, int key) { if (root==null) return null;//我们需要找的是val为key的元素 if (root.val==key){//找到值了,接下来对左右结点进行判断 TreeNode left=root.left; TreeNode right=root.right; if (left==null&&right==null){//左右都为空 直接删 return null; }else if (left==null){//只有右节点 return right; }else if (right==null){//只有左节点 return left; }else { TreeNode cur=right;//找右子树的最左结点 while (cur.left!=null){ cur=cur.left; } cur.left=left;//右子树的最左结点的左子树是root的左子树 return right; } } if (root.val<key) root.right=deleteNode(root.right,key); if (root.val>key) root.left=deleteNode(root.left,key); return root; }
3、修建二叉树
- 递归函数的定义:删除 BST 中小于 low 和大于 high 的所有节点,返回结果 BST
- 明确了递归函数的定义之后进行思考,如果一个节点的值没有落在 [lo, hi] 中,有两种情况:
- root.val < lo,这种情况下 root 节点本身和 root 的左子树全都是小于 lo 的,都需要被剪掉
- root.val > hi,这种情况下 root 节点本身和 root 的右子树全都是大于 hi 的,都需要被剪掉
class Solution { public TreeNode trimBST(TreeNode root, int low, int high) { if (root==null) return null;//遇到空结点返回即可 if (root.val<low){//说明root及它的左节点全部需要修建 return trimBST(root.right,low,high);//去它的右子树寻找结点作为它的上一个结点的左右子树即可 } if (root.val>high){//说明root及右子树全部不满足要求 return trimBST(root.left,low,high); } root.left=trimBST(root.left,low,high); root.right=trimBST(root.right,low,high); return root; } }
4、将有序数组转为搜索二叉树
- BST的中序遍历是升序的,因此本题等同于根据中序遍历的序列恢复二叉搜索树。因此我们可以以升序序列中的任一个元素作为根节点,以该元素左边的升序序列构建左子树,以该元素右边的升序序列构建右子树,这样得到的树就是一棵二叉搜索树啦~ 又因为本题要求高度平衡,因此我们需要选择升序序列的中间元素作为根节点奥~
-
class Solution { public TreeNode sortedArrayToBST(int[] nums){//由于涉及数组分割,我们必须有一个新的函数来限制数组左右 return helper(nums,0,nums.length-1);//左闭右闭 } public TreeNode helper(int[] nums,int left,int right){ if (left>right){//左闭右闭,left》right时没有元素 return null; } int mid=left+(right-left)/2; TreeNode root=new TreeNode(nums[mid]); root.left=helper(nums,left,mid-1); root.right=helper(nums,mid+1,right); return root; } }