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

情形1:树是搜索二叉树

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

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)

測试:


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:树是普通的二叉树,且树中节点有指向父节点指针

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

//含有指向父节点指针的树节点
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)

測试:


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:树是普通的二叉树,节点没有指向父节点的指针

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

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)

測试:


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


原文地址:https://www.cnblogs.com/bhlsheji/p/5142134.html