九度Online Judge | 剑指Offer | 树中两个结点的最低公共祖先

题目描述:

给定一棵树,同时给出树中的两个结点,求它们的最低公共祖先。

输入:

输入可能包含多个测试样例。
对于每个测试案例,输入的第一行为一个数n(0<n<1000),代表测试样例的个数。
其中每个测试样例包括两行,第一行为一个二叉树的先序遍历序列,其中左右子树若为空则用0代替,其中二叉树的结点个数node_num<10000。
第二行为树中的两个结点的值m1与m2(0<m1,m2<10000)。

输出:

对应每个测试案例,
输出给定的树中两个结点的最低公共祖先结点的值,若两个给定结点无最低公共祖先,则输出“My God”。

样例输入:
2
1 2 4 6 0 0 7 0 0 5 8 0 0 9 0 0 3 0 0
6 8
1 2 4 6 0 0 7 0 0 5 8 0 0 9 0 0 3 0 0
6 12
样例输出:
2
My God
 
 
分析:
如果是二叉查找树,可以利用查找树的性质,有O(lgn)的方法。
这里是一棵普通的二叉树。分为两种情况:(1)已知每个节点的父节点;(2)不知道每个节点的父节点。
如果不知道父节点,采用递归的方法,从上到下(最坏O(n^2)),或从下到上(最坏O(n))均可,参见http://leetcode.com/2011/07/lowest-common-ancestor-of-a-binary-tree-part-i.html
如果知道父节点,可采用非递归的方法,也有两种方法,利用哈希表,或利用节点的高度差(不需要额外空间),复杂度为O(h),h为树的高度,参见http://leetcode.com/2011/07/lowest-common-ancestor-of-a-binary-tree-part-ii.html
此外,第一篇文章里还给出了一个查找复杂度为O(sqt(n)),初始化复杂度为O(n)的方法:http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor,这个方法很像“1到100层楼,有两个鸡蛋,如何在最少次数内找到鸡蛋临界摔碎的点”的感觉,先分层,再在层里找,不过这里分层是均匀分的。
下面展示的是复杂度为O(h)的非递归、利用节点高度差的方法,已经AC。针对这道题,创建树的过程只需要记录每个节点的父节点即可,左右子节点可以无视,但创建过程复杂度已经O(n)了,还递归了,喧宾夺主了。- -!

解答:

import java.util.HashMap;
import java.util.Scanner;

public class LowestCommonAncestor {
	private class TreeNode {
		int val;
		TreeNode parent;

		TreeNode(int x) {
			val = x;
		}
	}

	private HashMap<Integer, TreeNode> records = new HashMap<Integer, TreeNode>();
	private int[] preOrder;
	private int index;

	public int lowestCommonAncestor(int[] preOrder, int p, int q) {
		if (preOrder.length <= 1) {
			return 0;
		}

		this.preOrder = preOrder;
		records.clear();
		index = 0;
		buildTree(null);

		TreeNode p1 = records.get(p);
		TreeNode p2 = records.get(q);
		if (p1 == null || p2 == null) {
			return 0;
		}
		int h1 = getHeight(p1);
		int h2 = getHeight(p2);

		if (h1 > h2) {
			int tmp = h1;
			h1 = h2;
			h2 = tmp;

			TreeNode tmpNode = p1;
			p1 = p2;
			p2 = tmpNode;
		}

		int dh = h2 - h1;
		for (int h = 0; h < dh; ++h) {
			p2 = p2.parent;
		}

		while (p1 != null && p2 != null) {
			if (p1.equals(p2)) {
				return p1.val;
			}
			p1 = p1.parent;
			p2 = p2.parent;
		}
		return 0;
	}

	private void buildTree(TreeNode parent) {
		int val = preOrder[index++];
		if (val != 0) {
			TreeNode node = new TreeNode(val);
			records.put(val, node);
			node.parent = parent;
			buildTree(node);
			buildTree(node);
		}
	}

	private int getHeight(TreeNode p) {
		int height = 0;
		while (p != null) {
			++height;
			p = p.parent;
		}
		return height;
	}

	public static int[] str2IntArray(String line) {
		String[] strArray = line.split(" ");
		int n = strArray.length;
		int[] intArray = new int[n];
		for (int i = 0; i < n; ++i) {
			intArray[i] = Integer.parseInt(strArray[i]);
		}
		return intArray;
	}

	public static void main(String[] args) {
		LowestCommonAncestor lca = new LowestCommonAncestor();
		Scanner reader = new Scanner(System.in);
		int n = reader.nextInt();
		reader.nextLine();
		for (int i = 0; i < n; ++i) {
			int[] preOrder = str2IntArray(reader.nextLine());
			int[] pq = str2IntArray(reader.nextLine());
			int p = pq[0];
			int q = pq[1];
			int ret = lca.lowestCommonAncestor(preOrder, p, q);
			System.out.println(ret == 0 ? "My God" : ret);
		}
		reader.close();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值