搜索二叉树
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x
的深度尽可能大(一个节点也可以是它自己的祖先)。”
解题思路:
搜索二叉树的的性质就是对于任意一个结点,它的左子树所有值都要比它小,右子树的所有结点值都要比它大。
既然是二叉搜索树,那么root
结点的值和p
,q
的值肯定有大小关系。 遍历过程中有三种情况:
- 是在发现当前结点的值和p,q任意一个相等,
- 发现当前结点比p,q都大/都小。
- 发现当前结点比其中一个大,其中一个小
- 当
root
结点的值比p和q都大时,就说明p,q结点肯定在root的左子树上,于是直接遍历左子树 - 当root结点的值比q和p小时,说明q和p在右子树上,于是遍历右子树
- 如果在遍历中发现root结点和q和p任意一个相等,直接可以判断这个root结点就是他们的公共祖先
- 当root结点比其中一个大,其中一个小,说明这两个结点在root的左右子树中, root也是他们的最近公共祖先。
总结:本题目就是要求我们熟悉搜索二叉树的性质,并规划好dfs的路径。
代码实现:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return null ;
TreeNode s= dfs(root,p,q) ;
return s;
}
private TreeNode dfs(TreeNode root, TreeNode p, TreeNode q){
if(root==null) return null ;
//在左右子树中
if( ( root.val>p.val &&root.val<q.val )|| (root.val <p.val && root.val > q.val) ) return root ;
// p或q
if(root.val==p.val || root.val==q.val) return root ;
// root的值比p,q都大,去左子树
if(root.val>p.val && root.val>q.val)
return lowestCommonAncestor(root.left, p,q) ;
// root的值比p,q都大,去右子树
if(root.val<p.val && root.val<q.val)
return lowestCommonAncestor(root.right, p, q) ;
return null ;
}
}
/*******************非递归版本:可以参考************************/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return null ;
TreeNode s =root;
while(true) {
if(p.val < s.val && q.val<s.val)
s=s.left;
else if(p.val>s.val&&q.val>s.val)
s=s.right;
else break;
}
return s;
}
}
思考
其实这道题一旦分析到上面的3种情况,实现就不难了,但是要分析出来,需要对搜索二叉树这种数据结构有一定理解。
填充每一个结点的下一个右侧指针
力扣117.
读题,想用dfs加前中后三种遍历方式来做,做不出来,只好灰溜溜地看了题解,发现是bfs,一方面骂自己菜,又悄悄庆幸好没有浪费太多的时间在错误的方向上。
唉,悲喜交加的刷题生涯。。。 (PS: 最近做题虽然做了记录发了博客,都没啥人看,反正写的不好,就当写日记一样记录我的垃圾大二青春把。。。)
思路
按照题解的思路,这种需要考虑同一层结点的题目,最好的方法还是使用bfs的层序遍历。
层序遍历一般都要用到队列,我再做题的时候,又陷入了如何记录这一层的结点个数的问题,原来直接用了queue.size()来表示当前一层的结点个数 。
在遍历的时候,记录结点pre
,指向后面poll出来的结点。
class Solution {
public Node connect(Node root) {
if(root==null) return root ;
Deque<Node> list=new LinkedList<Node>() ;
list.offerLast(root);
while(!list.isEmpty ()){
Node pre=null ;
int size =list.size() ;
for(int i=0 ;i<size;i++) {
Node cur=list.poll() ;
if(pre!=null) pre.next =cur ;
pre=cur ;
if(cur.left !=null) list.offerLast(cur.left) ;
if(cur.right !=null) list.offerLast(cur.right) ;
}
}
return root ;
}
}
把二叉搜索树转换成累加树
给出二叉 搜索 树的根节点,该二叉树的节点值各不相同,修改二叉树,使每个节点 node 的新值等于原树中大于或等于 node.val
的值之和。 示例:
解题过程:
我们知道解决树的问题其实就是靠遍历,前序,中序,后序和层序遍历,前三种遍历的不同点只在于访问的时机不同。
二叉搜索树有个特别的性质,中序遍历得到是一个递增的序列。
分析:对一个结点,要做的事情就是:将它的右子树所有的和加到自己身上,再将它的值加到左子树的各个结点上。 所以访问的路径就是: 右子树–>根结点–>左子树,代码表示如下:
dfs(root.right);
...//要做的动作
dfs(root.left);
我们可以设置一个变量,这个变量就随着递归函数游走在每一结点上,从右子树最右边的叶子开始有顺序地累每一个结点,返回的时候就将这个变量累加到当前结点上,这样就实现了将整棵树有顺序计划地加值。
class Solution {
int num=0;
public TreeNode convertBST(TreeNode root) {
if(root==null) return root;
convertBST(root.right) ;
root.val+=num;
num= root.val;
convertBST(root.left) ;
return root ;
}
}
最后总结
这道题的定位是easy
,逻辑也不难懂,最核心的部分就是实现。(我在做这道题的时候,就没有设置变量,有些结点就没有加全)个人觉得,使用递归不难,在递归中还要使用一些变量就比较难受,经验太少,不懂得如何设置好变量。
最大二叉树
解题思路:
-
题目要求每次都在数组里寻找最大的元素,将该元素作为根节点,该元素右边的序列作为右子树,左边的序列作为左子树。
-
本题建树的过程,本身就是一个递归的过程,用同样的动作确定根节点,确定左右子树。
public class Solution {
private int findIndex(int[] arr, int start , int end ){
int idx =start, num = -1 ;
for(int i=start ;i<end ;i++){
if (arr[i]>num){
idx= i ;
num = arr[i] ;
}
}
return idx ;
}
private TreeNode recursive(int [] nums,int start,int end) {
if(start>=end){
return null ;
}
int idx = findIndex(nums, start,end);
TreeNode root = new TreeNode(nums[idx]);
root.left= recursive(nums,start, idx) ;
root.right = recursive(nums,idx+1,end) ;
return root ;
}
public TreeNode constructMaximumBinaryTree(int[] nums) {
return recursive(nums, 0, nums.length-1);
}
}
重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。