【Java】面试题68:树中两个节点的最低公共祖先

情景一:

假设该树是一棵二叉搜索树

我们只需从根结点判断,如果二结点与根的左右子树比较一大一小,那么跟结点就是二者最低公共祖先;如果二结点都比左子结点小,向左子树递归进行比较;如果二结点都比右子树结点大,向右子树递归进行比较;

	public static TreeNode getLastCommonNode(TreeNode pRoot,TreeNode pLeft,
			TreeNode pRight) {
		TreeNode treeNode = null;
		if(pRoot==null || pLeft.val>pRight.val)
			return null;
		if((pRoot.val>=pRight.val) && (pRoot.val>=pLeft.val))
			treeNode = getLastCommonNode(pRoot.left,pLeft,pRight);
		if((pRoot.val<=pLeft.val) && (pRoot.val<=pRight.val))
			treeNode = getLastCommonNode(pRoot.right,pLeft,pRight);
		if(pRoot.val>=pLeft.val && pRoot.val<=pRight.val)
			return pRoot;
		return treeNode;
	}

情景二:

假设该树是一棵普通的树,且有指向父节点的指针

如果不是二叉搜索树,但带有指向父节点的指针,那么此题转换成在两个有相交的链表上求第一个相交点。
从下到上查找

package jianZhiOffer;

import java.util.ArrayList;
import java.util.HashMap;

/*
 * //方法2:假设是普通的树,但是每个节点都有指向父节点的指针,类似于链表找公共节点
 */
public class Demo6802 {
	private class TreeNode<T>{
		T data;
		TreeNode<T> parent = null;
		ArrayList<TreeNode<T>> children = new ArrayList<TreeNode<T>>();
		TreeNode(T data){
			this.data = data;
		}
	}
	
	public static void main(String[] args) {
		Demo6802 my = new Demo6802();
		    TreeNode A = my.new TreeNode('A');
	        TreeNode B = my.new TreeNode('B');
	        TreeNode C = my.new TreeNode('C');
	        TreeNode D = my.new TreeNode('D');
	        TreeNode E = my.new TreeNode('E');
	        TreeNode F = my.new TreeNode('F');
	        TreeNode G = my.new TreeNode('G');
	        TreeNode H = my.new TreeNode('H');

	        G.parent = E;
	        E.parent = B;
	        B.parent = A;
	        F.parent = D;
	        D.parent = B;
	        C.parent = A;
	 
	        System.out.println(findParent(A, F, G));
	}
	
	//Object类型的方法返回的类型不受限制,Object是所有类的父类
	private static Object findParent(TreeNode<Character> root,
			TreeNode<Character> node1,TreeNode<Character> node2) {
		if(node1==null || node2==null)
			return null;
		//得到两链表的长度
		int len1=1,len2=1;
		TreeNode<Character> temp=node1;
		while(temp.parent!=null) {
			len1++;
			temp=temp.parent;
		}
		temp=node2;
		while(temp.parent!=null) {
			len2++;
			temp=temp.parent;
		}
		//长度长的链表先移动
		int step = Math.abs(len1-len2);
		TreeNode tempLong = len1>=len2? node1:node2;
		TreeNode tempShort = len1<len2? node1:node2;
		for(int i=0;i<step;i++)
			tempLong = tempLong.parent;
		
		//两链表一起移动,知道找到相等的节点则返回
		for(int i=0;i<Math.min(len1, len2);i++) {
			if(tempLong.data==tempShort.data) {
				return tempLong.data;
			}
			tempLong = tempLong.parent;
			tempShort = tempShort.parent;
		}
		return null;
	}
}

情景三:
假设该树是普通的树,且没有指向父节点的指针:

需要保存从root根节点到p和q节点的路径,并将路径存入list中,则问题转化成求两个list集合的最后一个公共元素
从上到下查找

package jianZhiOffer;

import java.util.ArrayList;
import java.util.List;

public class Demo6801 {
	private static class TreeNode{
		int val;
		List<TreeNode> children = new ArrayList<>();	
		public TreeNode(int val) {
			this.val = val;
		}
		@Override
		//重写
		public String toString() {
			return val+"";
		}
	}
	
	public static void main(String[] args) {
		// 形状普通的树
	    //             1
	    //           /   \
	    //         2      3
	    //        /         \
	    //      4            5
	    //     / \        /  |  \
	    //    6   7      8   9  10
        TreeNode n1 = new TreeNode(1);
        TreeNode n2 = new TreeNode(2);
        TreeNode n3 = new TreeNode(3);
        TreeNode n4 = new TreeNode(4);
        TreeNode n5 = new TreeNode(5);
        TreeNode n6 = new TreeNode(6);
        TreeNode n7 = new TreeNode(7);
        TreeNode n8 = new TreeNode(8);
        TreeNode n9 = new TreeNode(9);
        TreeNode n10 = new TreeNode(10);
 
        n1.children.add(n2);
        n1.children.add(n3);
        n2.children.add(n4);
        n4.children.add(n6);
        n4.children.add(n7);
        n3.children.add(n5);
        n5.children.add(n8);
        n5.children.add(n9);
        n5.children.add(n10);
        System.out.println(getLastCommonParent(n1, n9, n10));
	}

	//方法3:假设是一棵普通的树,子节点没有指向父节点的指针
	//需要保存从root根节点到p和q节点的路径,并将路径存入list中,
	//则问题转化成求两个list集合的最后一个公共元素
	public static TreeNode getLastCommonParent(TreeNode root,TreeNode p1,
			TreeNode p2) {
		//存储根节点到p1和p2的路径(不包括p1和p2)
		List<TreeNode> path1 = new ArrayList<TreeNode>();
		List<TreeNode> path2 = new ArrayList<TreeNode>();
		List<TreeNode> tmpList = new ArrayList<TreeNode>();
		
		getNodePath(root,p1,tmpList,path1);
		getNodePath(root,p2,tmpList,path2);
		//如果路径不存在,返回空
		if(path1.size()==0 || path2.size()==0)
			return null;
		
		return getLastCommonParent(path1,path2);
	}
	
	//获取根节点到目标节点的路径
	public static void getNodePath(TreeNode root,TreeNode target,
			List<TreeNode>tmpList,List<TreeNode>path) {
		//鲁棒性
		if(root==null || root==target)
			return ;
		tmpList.add(root);
		//System.out.println(root);
		List<TreeNode> children = root.children;
		for(TreeNode node : children) {
			if(node==target) {
				path.addAll(tmpList);
				break;
			}
			getNodePath(node,target,tmpList,path);
		}
		tmpList.remove(tmpList.size()-1);
	}
	
	//将问题转换为求链表最后一个公共节点
	private static TreeNode getLastCommonParent(List<TreeNode>p1,
			List<TreeNode>p2) {
		TreeNode tmpNode = null;
		for(int i=0;i<p1.size();i++) {
			if(p1.get(i) != p2.get(i))
				break;
			tmpNode = p1.get(i);
		}
		return tmpNode;
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值