二叉搜索树的最近公共祖先
\
//方法一:递归!
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;
}
时间复杂度:O(N)
最坏所有节点都递归遍历一遍!
空间复杂度:O(N)
单分支树,退化成了链表!