您的位置:首页 > 其它

最低公共祖先问题(LCA) 举一反三

2017-09-13 18:43 309 查看
算法总是美的,充满着magic.

一.前言

给定一棵树,同时给出树中的两个结点或者两个以上节点,求它们的最低公共祖先。这就是常见的LCA(Lowest Common Ancestor )问题。

二.两个节点的LCA问题

常规解法

1.1 思路

下面是一个简单的复杂度为 O(n) 的算法,解决LCA问题

1) 找到从根到n1的路径,并存储在一个向量或数组中。

2)找到从根到n2的路径,并存储在一个向量或数组中。

3) 遍历这两条路径,直到遇到一个不同的节点,则前面的那个即为最低公共祖先.

1.2 代码

//遍历两次,时间复杂度为O(n)
private static int findLCA(BinaryTree root, int key1, int key2) {
if (root == null) {
return -1;
}
List<Integer> path1 = new ArrayList<>();
List<Integer> path2 = new ArrayList<>();
boolean found1 = findPath(root,path1,key1);
boolean found2 = findPath(root,path2,key2);
int ans = 0;
if (found1 && found2) {
for (int i = 0; i < path1.size(); i++) {
if (path1.get(i) != path2.get(i)) {
break;
}else {
ans = path1.get(i);
}
}
return ans;
}

return -1;
}

/**
* @category 从根节点找到一条到指点数的路径.
* @param root 二叉树根节点
* @param path 存储根节点到key的路径
* @param key 要查找的数
* @return 若路径存在则返回true,否则,false.
*
*/
private static boolean findPath(BinaryTree root, List<Integer> path, int key) {
if (root == null) {
return false;
}
path.add(root.value);
if (root.value == key) {
return true;
}
boolean find = (findPath(root.left, path, key)||findPath(root.right, path, key));
if (find) {
return true;
}

path.remove(path.indexOf(root.value));
return false;
}


优化解法

只遍历一次

1.1 思路

从root开始遍历,如果n1和n2中的任一个和root匹配,那么root就是LCA。 如果都不匹配,则分别递归左、右子树,如果有一个 key(n1或n2)出现在左子树,并且另一个key(n1或n2)出现在右子树,则root就是LCA. 如果两个key都出现在左子树,则说明LCA在左子树中,否则在右子树。

1.2 代码

/**
* 从root节点向下遍历,若root.value等于任一要查找的值,则root为祖先节点,
* 如果都不匹配,则遍历左子树和右子树,如果一个key出现在左子树,一个出现在右子树,则root为祖先节点,
* 如果两个都出现在左子树,则说明LCA在左子树,否则在右子树.
* @param root 二叉树根节点
* @param key1,key2 要查找的数
* @return 公共祖先节点
*/
private static BinaryTree findLCA(BinaryTree root, int key1, int key2) {
if (root == null) {
return null;
}

if (root.value == key1 || root.value == key2) {
return root;
}

BinaryTree left = findLCA(root.left, key1, key2);
BinaryTree right = findLCA(root.right, key1, key2);
if (left != null && right != null) {
return root;
}

return (left !=null)?left:right;
}


三.三个节点的LCA问题

题目如题.

1.1 思路

思路同上述常规解法. 相当找三个链表的最后一个公共节点.

1.2 代码

/**
* @param root 二叉树根节点
* @param key1,key2,key3 要查找的数
* @return 公共祖先节点
*/
private static int findLCA(BinaryTree root, int key1, int key2, int key3) {
if (root == null) {
return -1;
}
List<Integer> path1 = new ArrayList<>();
List<Integer> path2 = new ArrayList<>();
List<Integer> path3 = new ArrayList<>();
boolean found1 = findPath(root,path1,key1);
boolean found2 = findPath(root,path2,key2);
boolean found3 = findPath(root,path3,key3);
int ans = 0;
if (found1 && found2 && found3) {
int index = 0;
while(index<path1.size()){
if (path1.get(index)==path2.get(index)&&path1.get(index)==path3.get(index)) {
ans = path1.get(index);
index++;
}else {
break;
}
}
return ans;
}

return -1;
}

private static boolean findPath(BinaryTree root, List<Integer> path, int key) {
if (root == null) {
return false;
}
path.add(root.value);
if (root.value == key) {
return true;
}
boolean found = (findPath(root.left, path, key)||findPath(root.right, path, key));
if (found) {
return true;
}
path.remove(path.indexOf(root.value));

return false;
}


四.三个节点的LCA问题

给定一满二叉排序树,节点从1-(2^k-1),给出三个数a,b,c;找出它们的最低公共祖先. (腾讯题)

直接上代码:

import java.util.Scanner;

/**
* LCA 问题: 满二叉排序树中三个节点的最低公共祖先
* @author dingding
* @version 9-13
*/
public class BT_NoParentPtr_Solution4 {

public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
while(cin.hasNext()){
int k = cin.nextInt();
int a = cin.nextInt();
int b = cin.nextInt();
int c = cin.nextInt();

int result = findLCA(a,b,c,1,(2<<k)-1);
System.out.println(result);
}

}

/**
* 若根节点的值为m,若存在(a-m)(b-m)<0 || (a-m)(c-m)<0 || (b-m)(c-m)<0,则节点m 为最低祖先.
* @param a,b,c 查找的三个数
* @param left 数组最左边的值,right 数组最右边. (数组为二叉排序数组)
* @return 公共祖先节点
*/
private static int findLCA(int a, int b, int c, int left, int right) {
int m = left+(right-left)/2;
if (((a-m)*(b-m)<=0) || ((a-m)*(c-m)<=0) || ((b-m)*(c-m)<=0)) {
return m;
}else if(a>m){  //不在根节点,a>m必有一节点在右子树,搜索右侧.
return findLCA(a, b, c, m+1, right);
}else {
return findLCA(a, b, c, left, m-1);
}

}
}


参考文献

寻找二叉树两个节点的最低公共祖先
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: