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

题目:

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

最近公共祖先简称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

思路二:

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

a6a67658b3fb899463bec33238ab55e9.png

cf7e037fbc922f9b641c0ae7be64cc1b.png

思路三:

我们可以用深度优先搜索,从叶子节点向上,标记子树中出现目标节点的情况。如果子树中有目标 节点,标记为那个目标节点,如果没有,标记为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

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

3b104ddfd43a574066bf93339b97978b.png

44bbebdef51a9354502771920d22c610.png

4b19ee4a64553046865e0ff372eee678.png

4005764188a798fb3300162bb2bb322e.png

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 stackp = new Stack();

Stack stackq = new Stack();

getPath(root, p, stackp);

getPath(root, q, stackq);

return lowestCommonAncestor(stackp, stackq);

}

private static TreeNode lowestCommonAncestor(Stack stackp, Stack 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 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值