树中两个节点的最低公共祖先

本文介绍在不同类型的二叉树中寻找两个节点的最低公共祖先的有效算法,包括搜索二叉树、普通二叉树(含指向父节点指针)及普通二叉树(不含指向父节点指针)。每种情况下提供了详细的实现思路与代码示例。

情形1:树是搜索二叉树

思路:从树的根节点开始遍历,如果根节点的值大于其中一个节点,小于另外一个节点,则根节点就是最低公共祖先。否则如果根节点的值小于两个节点的值,则递归求根节点的右子树,如果大于两个节点的值则递归求根的左子树。如果根节点正好是其中的一个节点,那么说明这两个节点在一条路径上,所以最低公共祖先则是根节点的父节点

  1. public static BinaryTreeNode getLowestCommonAncestor(BinaryTreeNode rootParent,BinaryTreeNode root,BinaryTreeNode node1,BinaryTreeNode node2){  
  2.         if(root == null || node1 == null || node2 == null){  
  3.             return null;  
  4.         }  
  5.         if((root.value - node1.value)*(root.value - node2.value) < 0){  
  6.             return root;  
  7.         }else if((root.value - node1.value)*(root.value - node2.value) > 0){  
  8.             BinaryTreeNode newRoot = ((root.value > node1.value) && (root.value > node2.value)) ? root.leftNode : root.rightNode;  
  9.             return getLowestCommonAncestor(root,newRoot, node1, node2);  
  10.         }else{  
  11.             return rootParent;  
  12.         }  
  13.     }  
public static BinaryTreeNode getLowestCommonAncestor(BinaryTreeNode rootParent,BinaryTreeNode root,BinaryTreeNode node1,BinaryTreeNode node2){
		if(root == null || node1 == null || node2 == null){
			return null;
		}
		if((root.value - node1.value)*(root.value - node2.value) < 0){
			return root;
		}else if((root.value - node1.value)*(root.value - node2.value) > 0){
			BinaryTreeNode newRoot = ((root.value > node1.value) && (root.value > node2.value)) ? root.leftNode : root.rightNode;
			return getLowestCommonAncestor(root,newRoot, node1, node2);
		}else{
			return rootParent;
		}
	}

复杂度:由于递归调用二叉树,所以时间复杂度是O(logn),空间复杂度是O(1)

测试


  1. public class BinaryTreeNode {  
  2.     public int value;  
  3.     public BinaryTreeNode leftNode;  
  4.     public BinaryTreeNode rightNode;  
  5.       
  6.     public BinaryTreeNode(int value){  
  7.         this.value = value;  
  8.         leftNode = null;  
  9.         rightNode = null;  
  10.     }  
  11. }  
  12.   
  13. public static void main(String[] args){  
  14.         BinaryTreeNode A = new BinaryTreeNode(4);  
  15.         BinaryTreeNode B = new BinaryTreeNode(2);  
  16.         BinaryTreeNode C = new BinaryTreeNode(6);  
  17.         BinaryTreeNode D = new BinaryTreeNode(1);  
  18.         BinaryTreeNode E = new BinaryTreeNode(3);  
  19.         BinaryTreeNode F = new BinaryTreeNode(5);  
  20.         BinaryTreeNode G = new BinaryTreeNode(7);  
  21.         A.leftNode = B;  
  22.         A.rightNode = C;  
  23.         B.leftNode = D;  
  24.         B.rightNode = E;  
  25.         C.leftNode = F;  
  26.         C.rightNode = G;  
  27.         BinaryTreeNode res1 = getLowestCommonAncestor(null, A, E, F);  
  28.         BinaryTreeNode res2 = getLowestCommonAncestor(null, A, D, E);  
  29.         BinaryTreeNode res3 = getLowestCommonAncestor(null, A, B, D);  
  30.         System.out.println("The lowest common ancestor of 3 and 5 is " +res1.value);  
  31.         System.out.println("The lowest common ancestor of 1 and 3 is " +res2.value);  
  32.         System.out.println("The lowest common ancestor of 1 and 2 is " +res3.value);  
  33. }  
public class BinaryTreeNode {
	public int value;
	public BinaryTreeNode leftNode;
	public BinaryTreeNode rightNode;
	
	public BinaryTreeNode(int value){
		this.value = value;
		leftNode = null;
		rightNode = null;
	}
}

public static void main(String[] args){
		BinaryTreeNode A = new BinaryTreeNode(4);
		BinaryTreeNode B = new BinaryTreeNode(2);
		BinaryTreeNode C = new BinaryTreeNode(6);
		BinaryTreeNode D = new BinaryTreeNode(1);
		BinaryTreeNode E = new BinaryTreeNode(3);
		BinaryTreeNode F = new BinaryTreeNode(5);
		BinaryTreeNode G = new BinaryTreeNode(7);
		A.leftNode = B;
		A.rightNode = C;
		B.leftNode = D;
		B.rightNode = E;
		C.leftNode = F;
		C.rightNode = G;
		BinaryTreeNode res1 = getLowestCommonAncestor(null, A, E, F);
		BinaryTreeNode res2 = getLowestCommonAncestor(null, A, D, E);
		BinaryTreeNode res3 = getLowestCommonAncestor(null, A, B, D);
		System.out.println("The lowest common ancestor of 3 and 5 is " +res1.value);
		System.out.println("The lowest common ancestor of 1 and 3 is " +res2.value);
		System.out.println("The lowest common ancestor of 1 and 2 is " +res3.value);
}

结果:

The lowest common ancestor of 3 and5 is 4

The lowest common ancestor of 1 and3 is 2

Thelowest common ancestor of 1 and 2 is 4


情形2:树是普通的二叉树,且树中节点有指向父节点指针

 思路:两个节点如果在两条路径上,那么类似于“求两个链表的第一个公共节点”的算法

  1. //含有指向父节点指针的树节点  
  2. public class NewBinaryTreeNode {  
  3.     public int value;  
  4.     public NewBinaryTreeNode parentNode;  
  5.     public NewBinaryTreeNode leftNode;  
  6.     public NewBinaryTreeNode rightNode;  
  7.       
  8.     public NewBinaryTreeNode(int value){  
  9.         this.value = value;  
  10.         parentNode = null;  
  11.         leftNode = null;  
  12.         rightNode = null;  
  13.     }  
  14. }  
  15.   
  16. public static NewBinaryTreeNode  getLowestCommonAncestor1(NewBinaryTreeNode root,NewBinaryTreeNode node1,NewBinaryTreeNode node2){  
  17.         if(root == null || node1 == null || node2 == null){  
  18.             return null;  
  19.         }  
  20.         int depth1 = findTheDepthOfTheNode(root, node1, node2);  
  21.         if(depth1 == -1){  
  22.             return node2.parentNode;  
  23.         }  
  24.         int depth2 = findTheDepthOfTheNode(root, node2, node1);  
  25.         if(depth2 == -1){  
  26.             return node1.parentNode;  
  27.         }  
  28.         //p指向较深的节点q指向较浅的节点  
  29.         NewBinaryTreeNode p = depth1 > depth2 ? node1 : node2;  
  30.         NewBinaryTreeNode q = depth1 > depth2 ? node2 : node1;  
  31.         int depth =  Math.abs(depth1 - depth2);  
  32.         while(depth > 0){  
  33.             p = p.parentNode;  
  34.             depth --;  
  35.         }  
  36.         while(p != q){  
  37.             p = p.parentNode;  
  38.             q = q.parentNode;  
  39.         }  
  40.         return p;  
  41.     }  
  42.       
  43.     //求node1的深度,如果node1和node2在一条路径上,则返回-1,否则返回node1的深度  
  44.     public static int findTheDepthOfTheNode(NewBinaryTreeNode root,NewBinaryTreeNode node1,NewBinaryTreeNode node2){  
  45.         int depth = 0;  
  46.         while(node1.parentNode != null){  
  47.             node1 = node1.parentNode;  
  48.             depth ++;  
  49.             if(node1 == node2){  
  50.                 return -1;  
  51.             }  
  52.         }  
  53.         return depth;  
  54.     }  
//含有指向父节点指针的树节点
public class NewBinaryTreeNode {
	public int value;
	public NewBinaryTreeNode parentNode;
	public NewBinaryTreeNode leftNode;
	public NewBinaryTreeNode rightNode;
	
	public NewBinaryTreeNode(int value){
		this.value = value;
		parentNode = null;
		leftNode = null;
		rightNode = null;
	}
}

public static NewBinaryTreeNode  getLowestCommonAncestor1(NewBinaryTreeNode root,NewBinaryTreeNode node1,NewBinaryTreeNode node2){
		if(root == null || node1 == null || node2 == null){
			return null;
		}
		int depth1 = findTheDepthOfTheNode(root, node1, node2);
		if(depth1 == -1){
			return node2.parentNode;
		}
		int depth2 = findTheDepthOfTheNode(root, node2, node1);
		if(depth2 == -1){
			return node1.parentNode;
		}
		//p指向较深的节点q指向较浅的节点
		NewBinaryTreeNode p = depth1 > depth2 ? node1 : node2;
		NewBinaryTreeNode q = depth1 > depth2 ? node2 : node1;
		int depth =  Math.abs(depth1 - depth2);
		while(depth > 0){
			p = p.parentNode;
			depth --;
		}
		while(p != q){
			p = p.parentNode;
			q = q.parentNode;
		}
		return p;
	}
	
	//求node1的深度,如果node1和node2在一条路径上,则返回-1,否则返回node1的深度
	public static int findTheDepthOfTheNode(NewBinaryTreeNode root,NewBinaryTreeNode node1,NewBinaryTreeNode node2){
		int depth = 0;
		while(node1.parentNode != null){
			node1 = node1.parentNode;
			depth ++;
			if(node1 == node2){
				return -1;
			}
		}
		return depth;
	}

复杂度:由于每个节点的深度最多为logn,所以时间复杂度为O(logn),空间复杂度O(1)

测试:


  1. public static void main(String[] args){  
  2.         NewBinaryTreeNode A = new NewBinaryTreeNode(1);  
  3.         NewBinaryTreeNode B = new NewBinaryTreeNode(2);  
  4.         NewBinaryTreeNode C = new NewBinaryTreeNode(3);  
  5.         NewBinaryTreeNode D = new NewBinaryTreeNode(4);  
  6.         NewBinaryTreeNode E = new NewBinaryTreeNode(5);  
  7.         NewBinaryTreeNode F = new NewBinaryTreeNode(6);  
  8.         NewBinaryTreeNode G = new NewBinaryTreeNode(7);  
  9.         A.leftNode = B;  
  10.         A.rightNode = C;  
  11.         B.leftNode = D;  
  12.         B.rightNode = E;  
  13.         B.parentNode = A;  
  14.         C.leftNode = F;  
  15.         C.rightNode = G;  
  16.         C.parentNode = A;  
  17.         D.parentNode = B;  
  18.         E.parentNode = B;  
  19.         F.parentNode = C;  
  20.         G.parentNode = C;  
  21.         NewBinaryTreeNode res1 = getLowestCommonAncestor1(A,E,F);  
  22.         NewBinaryTreeNode res2 = getLowestCommonAncestor1(A,D,E);  
  23.         NewBinaryTreeNode res3 = getLowestCommonAncestor1(A,B,D);  
  24.         System.out.println("The lowest common ancestor of 5 and 6 is " +res1.value);  
  25.         System.out.println("The lowest common ancestor of 4 and 5 is " +res2.value);  
  26.         System.out.println("The lowest common ancestor of 2 and 4 is " +res3.value);  
  27.     }  
public static void main(String[] args){
		NewBinaryTreeNode A = new NewBinaryTreeNode(1);
		NewBinaryTreeNode B = new NewBinaryTreeNode(2);
		NewBinaryTreeNode C = new NewBinaryTreeNode(3);
		NewBinaryTreeNode D = new NewBinaryTreeNode(4);
		NewBinaryTreeNode E = new NewBinaryTreeNode(5);
		NewBinaryTreeNode F = new NewBinaryTreeNode(6);
		NewBinaryTreeNode G = new NewBinaryTreeNode(7);
		A.leftNode = B;
		A.rightNode = C;
		B.leftNode = D;
		B.rightNode = E;
		B.parentNode = A;
		C.leftNode = F;
		C.rightNode = G;
		C.parentNode = A;
		D.parentNode = B;
		E.parentNode = B;
		F.parentNode = C;
		G.parentNode = C;
		NewBinaryTreeNode res1 = getLowestCommonAncestor1(A,E,F);
		NewBinaryTreeNode res2 = getLowestCommonAncestor1(A,D,E);
		NewBinaryTreeNode res3 = getLowestCommonAncestor1(A,B,D);
		System.out.println("The lowest common ancestor of 5 and 6 is " +res1.value);
		System.out.println("The lowest common ancestor of 4 and 5 is " +res2.value);
		System.out.println("The lowest common ancestor of 2 and 4 is " +res3.value);
	}
结果:

The lowest common ancestor of 5 and6 is 1

The lowest common ancestor of 4 and5 is 2

The lowest common ancestor of 2 and4 is 1

情形3:树是普通的二叉树, 节点没有指向父节点的指针

思路:用栈来实现类似于指向父节点指针的功能

  1. public static BinaryTreeNode getLowestCommonAncestor2(BinaryTreeNode root, BinaryTreeNode node1, BinaryTreeNode node2){  
  2.         if(root == null || node1 == null || node2 == null){  
  3.             return null;  
  4.         }  
  5.         Stack<BinaryTreeNode> path1 = new Stack<BinaryTreeNode>();  
  6.         boolean flag1 = getThePathOfTheNode(root, node1,path1);  
  7.         if(!flag1){//树上没有node1节点  
  8.             return null;  
  9.         }  
  10.         Stack<BinaryTreeNode> path2 = new Stack<BinaryTreeNode>();  
  11.         boolean flag2 = getThePathOfTheNode(root, node2,path2);  
  12.         if(!flag2){//树上没有node2节点  
  13.             return null;  
  14.         }  
  15.         if(path1.size() > path2.size()){ //让两个路径等长  
  16.             while(path1.size() !=  path2.size()){  
  17.                 path1.pop();  
  18.             }  
  19.         }else{  
  20.             while(path1.size() !=  path2.size()){  
  21.                 path2.pop();  
  22.             }  
  23.         }  
  24.         if(path1.equals(path2)){//当两个节点在一条路径上时  
  25.             path1.pop();  
  26.             return path1.pop();  
  27.         }else{  
  28.             BinaryTreeNode p = path1.pop();  
  29.             BinaryTreeNode q = path2.pop();  
  30.             while(q != p){  
  31.                 p = path1.pop();  
  32.                 q = path2.pop();  
  33.             }  
  34.             return p;  
  35.         }  
  36.     }   
  37.       
  38.     //获得根节点到node节点的路径  
  39.     public static boolean getThePathOfTheNode(BinaryTreeNode root,BinaryTreeNode node,Stack<BinaryTreeNode> path){  
  40.         path.push(root);  
  41.         if(root == node){  
  42.             return true;  
  43.         }  
  44.         boolean found = false;  
  45.         if(root.leftNode != null){  
  46.             found = getThePathOfTheNode(root.leftNode, node, path);  
  47.         }  
  48.         if(!found && root.rightNode != null){  
  49.             found = getThePathOfTheNode(root.rightNode, node, path);  
  50.         }  
  51.         if(!found){  
  52.             path.pop();  
  53.         }  
  54.         return found;  
  55.     }  
public static BinaryTreeNode getLowestCommonAncestor2(BinaryTreeNode root, BinaryTreeNode node1, BinaryTreeNode node2){
		if(root == null || node1 == null || node2 == null){
			return null;
		}
		Stack<BinaryTreeNode> path1 = new Stack<BinaryTreeNode>();
		boolean flag1 = getThePathOfTheNode(root, node1,path1);
		if(!flag1){//树上没有node1节点
			return null;
		}
		Stack<BinaryTreeNode> path2 = new Stack<BinaryTreeNode>();
		boolean flag2 = getThePathOfTheNode(root, node2,path2);
		if(!flag2){//树上没有node2节点
			return null;
		}
		if(path1.size() > path2.size()){ //让两个路径等长
			while(path1.size() !=  path2.size()){
				path1.pop();
			}
		}else{
			while(path1.size() !=  path2.size()){
				path2.pop();
			}
		}
		if(path1.equals(path2)){//当两个节点在一条路径上时
			path1.pop();
			return path1.pop();
		}else{
			BinaryTreeNode p = path1.pop();
			BinaryTreeNode q = path2.pop();
			while(q != p){
				p = path1.pop();
				q = path2.pop();
			}
			return p;
		}
	} 
	
	//获得根节点到node节点的路径
	public static boolean getThePathOfTheNode(BinaryTreeNode root,BinaryTreeNode node,Stack<BinaryTreeNode> path){
		path.push(root);
		if(root == node){
			return true;
		}
		boolean found = false;
		if(root.leftNode != null){
			found = getThePathOfTheNode(root.leftNode, node, path);
		}
		if(!found && root.rightNode != null){
			found = getThePathOfTheNode(root.rightNode, node, path);
		}
		if(!found){
			path.pop();
		}
		return found;
	}
复杂度:获取node节点的路径时间复杂度为O(n),所以总的时间复制度是O(n),空间复杂度是O(logn)

测试:


  1. public static void main(String[] args){  
  2.         BinaryTreeNode A = new BinaryTreeNode(1);  
  3.         BinaryTreeNode B = new BinaryTreeNode(2);  
  4.         BinaryTreeNode C = new BinaryTreeNode(3);  
  5.         BinaryTreeNode D = new BinaryTreeNode(4);  
  6.         BinaryTreeNode E = new BinaryTreeNode(5);  
  7.         BinaryTreeNode F = new BinaryTreeNode(6);  
  8.         BinaryTreeNode G = new BinaryTreeNode(7);  
  9.         A.leftNode = B;  
  10.         A.rightNode = C;  
  11.         B.leftNode = D;  
  12.         B.rightNode = E;  
  13.         C.leftNode = F;  
  14.         C.rightNode = G;  
  15.         BinaryTreeNode res1 = getLowestCommonAncestor2( A, E, F);  
  16.         BinaryTreeNode res2 = getLowestCommonAncestor2( A, D, E);  
  17.         BinaryTreeNode res3 = getLowestCommonAncestor2( A, B, D);  
  18.         System.out.println("The lowest common ancestor of 5 and 6 is " +res1.value);  
  19.         System.out.println("The lowest common ancestor of 4 and 5 is " +res2.value);  
  20.         System.out.println("The lowest common ancestor of 2 and 4 is " +res3.value);  
  21.     }  
public static void main(String[] args){
		BinaryTreeNode A = new BinaryTreeNode(1);
		BinaryTreeNode B = new BinaryTreeNode(2);
		BinaryTreeNode C = new BinaryTreeNode(3);
		BinaryTreeNode D = new BinaryTreeNode(4);
		BinaryTreeNode E = new BinaryTreeNode(5);
		BinaryTreeNode F = new BinaryTreeNode(6);
		BinaryTreeNode G = new BinaryTreeNode(7);
		A.leftNode = B;
		A.rightNode = C;
		B.leftNode = D;
		B.rightNode = E;
		C.leftNode = F;
		C.rightNode = G;
		BinaryTreeNode res1 = getLowestCommonAncestor2( A, E, F);
		BinaryTreeNode res2 = getLowestCommonAncestor2( A, D, E);
		BinaryTreeNode res3 = getLowestCommonAncestor2( A, B, D);
		System.out.println("The lowest common ancestor of 5 and 6 is " +res1.value);
		System.out.println("The lowest common ancestor of 4 and 5 is " +res2.value);
		System.out.println("The lowest common ancestor of 2 and 4 is " +res3.value);
	}
结果:

The lowest common ancestor of 5 and6 is 1

The lowest common ancestor of 4 and5 is 2

The lowest common ancestor of 2 and 4 is 1


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值