您的位置:首页 > Web前端

剑指offer(java):求树中两个结点的最低公共祖先

2016-08-09 00:00 190 查看
题目:

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

最近公共祖先简称LCA(Lowest Common Ancestor),所谓LCA,是当给定一个有根树T时,对于任意两个结点u、v,找到一个离根最远的结点x,使得x同时是u和v的祖先,x 便是u、v的最近公共祖先。

思路一:

输入两个树节点,求他们的最低公共祖先,

——如果是二叉树,而且是二叉搜索树,那么是可以找到公共节点的。

二叉搜索树都是排序过的,位于左子树的节点都比父节点小,而位于右子树上面的节点都比父节点大。

如果当前节点的值比两个结点 的值都大,那么最低的共同的父节点一定是在当前节点的左子树中,于是下一步遍历当前节点的左子节点。

如果当前节点的值比两个结点的值都小,那么最低的共同的父节点一定是在当前节点的右子树中,于是下一步遍历当前节点的右子节点。

这样从上到下,找到的第一个在两个输入结点的值之间的节点,就是最低的公共祖先。

package cglib;

class TreeNode {
int data;
TreeNode left;
TreeNode right;
}

public class List1
{
public static TreeNode getLastCommonParent(TreeNode root,TreeNode a,TreeNode b)
{
if (root==null || a==null || b==null)
return null;
while (root != null) {
if (root.data > a.data && root.data > b.data )
root = root.left;
else if (root.data < a.data && root.data < b.data)
root = root.right;
else
return root;
}
return null;
}

public static void main(String args[]){
TreeNode n1 = new TreeNode();
TreeNode n2 = new TreeNode();
TreeNode n3 = new TreeNode();
TreeNode n4 = new TreeNode();
TreeNode n5 = new TreeNode();
TreeNode n6 = new TreeNode();
TreeNode n7 = new TreeNode();

n1.left=n2;
n1.right=n3;
n2.left=n4;
n2.right=n5;
n3.left=n6;
n3.right=n7;
n1.data=10;
n2.data=8;
n3.data=13;
n4.data=4;
n5.data=9;
n6.data=12;
n7.data=17;
// 搜索二叉树
// 10
// / \
// 8 13
// / \ / \
// 4 9 12 17
System.out.println(n1.data);
System.out.println(n6.data);
System.out.println(n7.data);
TreeNode parent=getLastCommonParent(n1, n6, n7);
System.out.println(parent.data);
}

}

输出:

10
12
17
13

思路二:

如果这棵树是普通的树,而且树中结点没有指向父结点的指针,解法如下:





思路三:

我们可以用深度优先搜索,从叶子节点向上,标记子树中出现目标节点的情况。如果子树中有目标 节点,标记为那个目标节点,如果没有,标记为null。显然,如果左子树、右子树都有标记,说明就已经找到最小公共祖先了。如果在根节点为p的左右子树中 找p、q的公共祖先,则必定是p本身。

换个角度,可以这么想:如果一个节点左子树有两个目标节点中的一个,右子树没有,那这个节点 肯定不是最小公共祖先。如果一个节点右子树有两个目标节点中的一个,左子树没有,那这个节点肯定也不是最小公共祖先。只有一个节点正好左子树有,右子树也 有的时候,才是最小公共祖先。

package cglib;

class TreeNode {
int data;
TreeNode left;
TreeNode right;
}

public class List1
{
//两个节点的最低公共祖先,参数为两个节点
public static TreeNode getLastCommonParent(TreeNode root,TreeNode a,TreeNode b)
{

//发现目标节点则通过返回值标记该子树发现了某个目标结点
//如果在根节点为a的左右子树中找a、b的公共祖先,则必定是a本身。
//同理,如果在根节点为b的左右子树中找a、b的公共祖先,则必定是b本身。
if (root == null || root == a || root == b)
return root;
//查看左子树中是否有目标结点,没有为null
TreeNode left = getLastCommonParent(root.left, a, b);
//查看右子树是否有目标节点,没有为null
TreeNode right = getLastCommonParent(root.right, a, b);
//都不为空,说明左右子树都有目标结点,则公共祖先就是本身
if (left != null&&right != null)
return root;
//如果发现了目标节点,则继续向上标记为该目标节点
return left == null ? right : left;

}

public static void main(String args[]){
TreeNode n1 = new TreeNode();
TreeNode n2 = new TreeNode();
TreeNode n3 = new TreeNode();
TreeNode n4 = new TreeNode();
TreeNode n5 = new TreeNode();
TreeNode n6 = new TreeNode();
TreeNode n7 = new TreeNode();

n1.left=n2;
n1.right=n3;
n2.left=n4;
n2.right=n5;
n3.left=n6;
n3.right=n7;
n1.data=1;
n2.data=2;
n3.data=3;
n4.data=4;
n5.data=5;
n6.data=6;
n7.data=7;
// 搜索二叉树
// 1
// / \
// 2 3
// / \ / \
// 4 5 6 7
System.out.println(n1.data);
System.out.println(n6.data);
System.out.println(n7.data);
TreeNode parent=getLastCommonParent(n1, n6, n7);
System.out.println(parent.data);
}

}

输出:

1
6
7
3

以上解法需要对同一个结点重复遍历很多次,效率较低,如果允许使用辅助内存,则可以有效率更高的方法,解法如下:









package cglib;

import java.util.Stack;

class TreeNode {
int data;
TreeNode left;
TreeNode right;
}

public class List1
{

public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
Stack<TreeNode> stackp = new Stack<TreeNode>();
Stack<TreeNode> stackq = new Stack<TreeNode>();
getPath(root, p, stackp);
getPath(root, q, stackq);
return lowestCommonAncestor(stackp, stackq);
}
private static TreeNode lowestCommonAncestor(Stack<TreeNode> stackp, Stack<TreeNode> stackq) {
TreeNode target = null;
while (!stackp.isEmpty() && !stackq.isEmpty() && stackp.peek() == stackq.peek())
{
target = stackp.peek();
stackp.pop();
stackq.pop();
}
return target;
}
private static boolean getPath(TreeNode root, TreeNode p, Stack<TreeNode> stackp) {
// TODO Auto-generated method stub
if (root == null)
return false;
if (root == p)
{
stackp.push(root);
return true;
}
else
{
if (getPath(root.left, p, stackp) || getPath(root.right, p, stackp))
{
stackp.push(root);
return true;
}
}
return false;
}
/***
*
* 这个代码在实现过程中,是当找到给定节点的时候才将路径依次压入stack中的,
* 也就是说,两个stack的栈顶都是存放着root节点。
* 因此,此时就应该找两条路径分离开之前的最后一个节点,
* 此节点就是所求的最低公共祖先。
* @param args
*/

public static void main(String args[]){
TreeNode n1 = new TreeNode();
TreeNode n2 = new TreeNode();
TreeNode n3 = new TreeNode();
TreeNode n4 = new TreeNode();
TreeNode n5 = new TreeNode();
TreeNode n6 = new TreeNode();
TreeNode n7 = new TreeNode();

n1.left=n2;
n1.right=n3;
n2.left=n4;
n2.right=n5;
n3.left=n6;
n3.right=n7;
n1.data=1;
n2.data=2;
n3.data=3;
n4.data=4;
n5.data=5;
n6.data=6;
n7.data=7;
// 搜索二叉树
// 1
// / \
// 2 3
// / \ / \
// 4 5 6 7
System.out.println(n1.data);
System.out.println(n6.data);
System.out.println(n7.data);
TreeNode parent=lowestCommonAncestor(n1, n6, n7);
System.out.println(parent.data);
}

}

输出:
1
6
7
3
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: