二叉搜索树的最近公共祖先
题目链接
//方法一:递归! public int lowestCommonAncestor (TreeNode root, int p, int q) { // write code here //利用二叉搜索树的特性,左子树小于根,根小于右子树! //从而定位到公共祖先节点! if(root==null) return -1; //pq在该节点两边,所以 root就是公共节点! if(root.val>=p&&root.val<=q||root.val<=p&&root.val>=q) return root.val; //pq都在该节点左边,说明在左子树 if(root.val>=q&&root.val>=p) return lowestCommonAncestor(root.left,p,q); else{ return lowestCommonAncestor(root.right,p,q); } }
时间复杂度O(N)最坏情况递归遍历完所有节点!
空间复杂度O(N),递归深度最坏为O(N)
通过两个数组集合保存两个节点的路径值
找到最后一个相等的节点,即是公共祖先!
// public ArrayList<Integer> getPath(TreeNode root,int x){ ArrayList<Integer> path = new ArrayList<>(); if(root==null) return path; while(root.val!=x){//节点值不同,说明还没遍历到 x! path.add(root.val); //这里不能用两个if判断,因为这里判断一次就会改变,root的指向!!! if(root.val>x){ //说明在左子树! root = root.left; }else{ //说明在右子树! root = root.right; } } //将 x 节点保存! path.add(x); return path; } public int lowestCommonAncestor (TreeNode root, int p, int q) { // write code here ArrayList<Integer> path1 = getPath(root,p); ArrayList<Integer> path2 = getPath(root,q); int ret = 0; for(int i = 0;i<path1.size()&&i<path2.size();i++){ //这里的 path1.get(i)不能直接比较相等,包装类需要通过equals比较! if(path1.get(i).equals(path2.get(i))){ ret = path2.get(i);//更新相同值 }else{ break;//无相同值,退出循环! } } return ret; }
注意:
Integer包装类比较不能直接通过==比较,要使用equals比较!
时间复杂度: O(N)最坏情况,遍历完所有节点,也就是二叉树单分支,变成了链表,所以需要比较所有节点!
空间复杂度:O(N)最坏情况,所有节点都记录保存!
在二叉树中找到两个节点的最近公共祖先
题目链接
描述:
这里和上面的搜索二叉树不同,不能直接利用搜索二叉树的特性,直接找到该路径!
我们这里只是普通的二叉树,需要遍历所有的节点,然后将其路径找出!
而我们知道找路径就是找到他这一条路径的祖先,也就是说将每个节点的父节点关系保存即可!
我们借助Map键值对保存,key保存当前节点,value保存根节点!
这里我们可以借助队列遍历到o1和o2节点就即可!
//方法一:BFS 广度优先! public int lowestCommonAncestor (TreeNode root, int o1, int o2) { // write code here //借助一种数据结构保存o1和o2的路径! //我们知道这里的二叉树只是普通的二叉树,并不是二叉搜索 //所以无法直接定位到节点路径 //我们的路径无法是该节点的所有父节点,那么我们可以换种思路! //通过map记录o1和o2节点的父节点,以及o1和o2的一系列祖宗,通过map键值对映射保存! //然后我们在找出o1的路径即可! //然后再在o1路径中找到o2或者o2的最近祖先即是最近公共祖先! if(root==null) return -1; Map<Integer,Integer> map = new HashMap<>();//key保存当前节点,value保存父节点! map.put(root.val,Integer.MAX_VALUE);//根节点无父节点! //通过队列,进行层序遍历,当遍历到o1和o2节点就退出! Queue<TreeNode> q = new LinkedList<>(); q.offer(root); while(!map.containsKey(o1)||!map.containsKey(o2)){ TreeNode cur = q.poll(); if(cur.left!=null){ //保存当前节点父子关系! map.put(cur.left.val,cur.val); q.offer(cur.left); } if(cur.right!=null){ map.put(cur.right.val,cur.val); q.offer(cur.right); } } //将o1和其祖宗路径保存! Set<Integer> ancestors = new HashSet<>(); while(map.containsKey(o1)){ ancestors.add(o1); //找到其父亲,一直到根节点! o1 = map.get(o1); } while(!ancestors.contains(o2)){ //在o1祖宗中找到o2或者o2的最近祖先 o2 = map.get(o2); } return o2; }
时间复杂度:O(N) 最坏所有节点都递归遍历一遍!
空间复杂度:O(N) map 和 queue分别最坏O(N)
我们可以采取递归的方式!
o1 ,o2 分别位于root的左右子树上
o1==root,o2在其子树上!
o2==root,o2在其子树
//方法二:递归 public int lowestCommonAncestor (TreeNode root, int o1, int o2) { // write code here if(root == null) return -1; //找到最近公共节点! return help(root,o1,o2).val; } public TreeNode help(TreeNode root,int o1,int o2){ if(root==null||root.val==o1||root.val==o2){ //递归到空||找到了o1或者o2 return root; } //看左子树是否存在o1或者o2节点 TreeNode left = help(root.left,o1,o2); TreeNode right = help(root.right,o1,o2); if(left==null){ //左子树为空,说明o1和o2节点在右子树! return right; } if(right==null){ return left; } //说明o1和o2分别在两边! return root; }