求二叉树中两个节点的最小公共祖先(LCA)

来源:互联网 发布:erp java 开发工具 编辑:程序博客网 时间:2024/05/21 22:45

题目要求:求二叉树中两个节点p,q的最低公共祖先节点

 

首先,题目中没有明确说明节点的结构,所以思考了一会然后问面试官节点有没有父指针,面试官说有没有父指针有影响吗?我说有,然后他笑着说你来说说看。当时,只做出来有父指针的情况,没有父指针的情况压根想不出来。后来会实验室静下心来很快就想到了思路。这里分这两种情况讨论:

 

1. 二叉树节点具有父指针

 

      在节点具有父指针的情况下,显然此二叉树就可以看成是通过父指针连接的"T"型链表,题目也就转化成查找"T"型链表的第一个公共节点。(可看做两个链表相交,求链表相交的第一个公共节点)假设p,q分别为所求的两个节点,则通过遍历一遍可以知道p,q分别到根节点的长度pLen和qLen。这样就知道pLen和qLen之间长度之差,也就知道p、q到它们的第一个公共祖先节点k长度之差L。因为p,q到根节点的路径中都包含k到根节点的路径,所以pLen和qLen之差等于p、q到k的长度之差。然后,让p、q到根节点路径长度大的先向前走L,然后长度小再和大的同时向前遍历,当它们两指向同一个节点时,则那个节点即是所求。

[java] view plaincopy
  1. /** 
  2.  * 有父指针情况下,查找两个节点的最低公共节点 
  3.  * @author chosen0ne 
  4.  * 2011-01-18 
  5.  */  
  6. class BTree<T>{  
  7.     private BTNode<T> root;  
  8.     public BTree(BTNode<T> r){  
  9.         this.root=r;  
  10.     }  
  11.     /** 
  12.      * 查找两个节点的最低公共祖先节点 
  13.      * @param p  
  14.      * @param q 
  15.      * @return BTNode<T> 最低公共祖先节点,没有找到返回null 
  16.      */  
  17.     public BTNode<T> findLowestAncestor(BTNode<T> p,BTNode<T> q){  
  18.         if(p==null||q==null)  
  19.             throw new NullPointerException();  
  20.         int pLen=0,qLen=0;//p,q两个节点到根节点的路径长度  
  21.         //计算p到根节点的长度  
  22.         for(BTNode<T> ancestor=p.parent;ancestor!=null;ancestor=ancestor.parent)  
  23.             pLen++;  
  24.         //计算q到根节点的长度  
  25.         for(BTNode<T> ancestor=q.parent;ancestor!=null;ancestor=ancestor.parent)  
  26.             qLen++;  
  27.         //如果p到根节点的长度比q长,则让p前进pLen-qLen  
  28.         for(;pLen>qLen;pLen--)  
  29.             p=p.parent;  
  30.         //如果q到根节点的长度比p长,则让q前进qLen-pLen  
  31.         for(;qLen>pLen;qLen--)  
  32.             q=q.parent;  
  33.         //此时,p和q到根节点的长度相同。假设k是最近的公共节点,则p和q到k的长度相同  
  34.         //p和q按照相同步长1向前遍历,如果存在公共节点则p和去会同时指向它  
  35.         while(q!=null&&p!=null&&p!=q){  
  36.             q=q.parent;  
  37.             p=p.parent;  
  38.         }  
  39.         if(p==q)  
  40.             return p;  
  41.         return null;  
  42.     }  
  43.     /** 
  44.      * 测试方法,在t中查找a,b的最低公共祖先节点 
  45.      * @param t 
  46.      * @param a 
  47.      * @param b 
  48.      */  
  49.     private static<T> void test(BTree<T> t, BTNode<T> a, BTNode<T> b){  
  50.         BTree.BTNode<T> p=t.findLowestAncestor(b,a);  
  51.         if(p!=null)  
  52.             System.out.println(a.data+","+b.data+"的最低公共祖先节点是 :"+p.data);  
  53.         else   
  54.             System.out.println(a.data+","+b.data+"没有公共祖先节点");  
  55.     }  
  56.     public static void main(String[] arg){  
  57.         /*  构造如下二叉树 
  58.                         a 
  59.                      /    / 
  60.                     b     c 
  61.                   /   /   /  / 
  62.                 d     e f    g 
  63.         */  
  64.         BTree.BTNode<String> g=new BTree.BTNode().data("g");  
  65.         BTree.BTNode<String> f=new BTree.BTNode().data("f");  
  66.         BTree.BTNode<String> e=new BTree.BTNode().data("e");  
  67.         BTree.BTNode<String> d=new BTree.BTNode().data("d");  
  68.         BTree.BTNode<String> c=new BTree.BTNode().data("c").left(f).right(g);  
  69.         f.parent(c);  
  70.         g.parent(c);  
  71.         BTree.BTNode<String> b=new BTree.BTNode().data("b").left(d).right(e);  
  72.         d.parent(b);  
  73.         e.parent(b);  
  74.         BTree.BTNode<String> a=new BTree.BTNode().data("a").left(b).right(c);  
  75.         b.parent(a);  
  76.         c.parent(a);  
  77.         BTree<String> t=new BTree<String>(a);  
  78.         test(t,c,f);  
  79.     }  
  80.     static class BTNode<T>  
  81.     {  
  82.         BTNode<T> left;  
  83.         BTNode<T> right;  
  84.         BTNode<T> parent;  
  85.         T data;  
  86.         public BTNode(){}  
  87.         public BTNode(BTNode<T> l,BTNode<T> r,BTNode<T> p,T d){  
  88.             this.left=l;  
  89.             this.right=r;  
  90.             this.parent=p;  
  91.             this.data=d;  
  92.         }  
  93.         BTNode<T> left(BTNode<T> l){  
  94.             this.left=l;  
  95.             return this;  
  96.         }  
  97.         BTNode<T> right(BTNode<T> r){  
  98.             this.right=r;  
  99.             return this;  
  100.         }  
  101.         BTNode<T> parent(BTNode<T> p){  
  102.             this.parent=p;  
  103.             return this;  
  104.         }  
  105.         BTNode<T> data(T d){  
  106.             this.data=d;  
  107.             return this;  
  108.         }   
  109.     }  
  110. }  

2.二叉树节点不具有父指针

      这种情况下,必须通过遍历查找一个节点的祖先集合,然后比较两个节点的祖先集合就可以找到最低的那个。这里采用后序遍历,并传入一个栈记录该节点的祖先节点。在每次访问一个节点时,先把这个节点压入栈,然后判断该节点是不是要查找的那个节点,如果是返回。接着查找它的左子树和右子树,当要查找的节点在它的左右子树中则返回。然后判断该节点与栈顶节点是否相同,是则弹出栈顶元素。这是因为相同就代表了在访问它的左右子树时没有添加新的节点,也就是说要查找的那个节点不在它的左右子树中,则该节点也就是不是要查找的节点的祖先。

[java] view plaincopy
  1. import java.util.ArrayList;  
  2. /** 
  3.  * 没有父指针情况下,查找两个节点的最低公共节点 
  4.  * @author chosen0ne 
  5.  * 2011-01-18 
  6.  */  
  7. class BTree<T>  
  8. {  
  9.     private BTNode<T> root;  
  10.     public BTree(BTNode<T> r){  
  11.         this.root=r;  
  12.     }  
  13.     /** 
  14.      * 查找两个节点的最低公共祖先节点 
  15.      * @param p  
  16.      * @param q 
  17.      * @return BTNode<T> 最低公共祖先节点,没有找到返回null 
  18.      */  
  19.     public BTNode<T> findLowestAncestor(BTNode<T> p,BTNode<T> q){  
  20.         if(p==null||q==null)  
  21.             throw new NullPointerException();  
  22.         ArrayList<BTNode<T>> sp=new ArrayList<BTNode<T>>();  
  23.         ArrayList<BTNode<T>> sq=new ArrayList<BTNode<T>>();  
  24.         travalPostOrder(root,p,sp);  
  25.         travalPostOrder(root,q,sq);  
  26.         //祖先栈中,以先后顺序存储,所以这里倒序来遍历以便找到最低的那个祖先节点  
  27.         for(int i=sp.size()-1;i>=0;i--)  
  28.             for(int j=sq.size()-1;j>=0;j--)  
  29.                 if(sp.get(i)==sq.get(j))  
  30.                     return sp.get(i);  
  31.         return null;  
  32.     }  
  33.     /** 
  34.      * 后序遍历二叉树,进行节点的搜索,当搜索成功时,将该节点的所有祖先存入栈中 
  35.      * @param n 遍历的节点 
  36.      * @param p 欲搜索的节点 
  37.      * @param stack 存储祖先节点的栈,这里使用ArrayList,因为后续查找最低公共祖先时需要遍历所有元素 
  38.      * @return boolean 是否搜索到该节点 
  39.      */  
  40.     private boolean travalPostOrder(BTNode<T> n,BTNode<T> p,ArrayList<BTNode<T>> stack){  
  41.         if(n!=null){  
  42.             stack.add(n);  
  43.             if(n==p)  
  44.                 return true;  
  45.             if(travalPostOrder(n.left,p,stack))  
  46.                 return true;  
  47.             if(travalPostOrder(n.right,p,stack))  
  48.                 return true;  
  49.             int lastIndex=stack.size()-1;  
  50.             //如果搜索完n的左右子树后,栈顶还是n,则代表n不是p的祖先节点,所以将n从栈中删除  
  51.             if(n==stack.get(lastIndex)){  
  52.                 stack.remove(lastIndex);  
  53.             }  
  54.         }  
  55.         return false;  
  56.     }  
  57.     /** 
  58.      * 测试方法,在t中查找a,b的最低公共祖先节点 
  59.      * @param t 
  60.      * @param a 
  61.      * @param b 
  62.      */  
  63.     private static<T> void test(BTree<T> t, BTNode<T> a, BTNode<T> b){  
  64.         BTree.BTNode<T> p=t.findLowestAncestor(b,a);  
  65.         if(p!=null)  
  66.             System.out.println(a.data+","+b.data+"的最低公共祖先节点是 :"+p.data);  
  67.         else   
  68.             System.out.println(a.data+","+b.data+"没有公共祖先节点");  
  69.     }  
  70.     public static void main(String[] arg){  
  71.         /*  构造如下二叉树 
  72.                         a 
  73.                      /    / 
  74.                     b     c 
  75.                   /   /   /  / 
  76.                 d     e f    g 
  77.         */  
  78.         BTree.BTNode<String> g=new BTree.BTNode().data("g");  
  79.         BTree.BTNode<String> f=new BTree.BTNode().data("f");  
  80.         BTree.BTNode<String> e=new BTree.BTNode().data("e");  
  81.         BTree.BTNode<String> d=new BTree.BTNode().data("d");  
  82.         BTree.BTNode<String> c=new BTree.BTNode().data("c").left(f).right(g);  
  83.         BTree.BTNode<String> b=new BTree.BTNode().data("b").left(d).right(e);  
  84.         BTree.BTNode<String> a=new BTree.BTNode().data("a").left(b).right(c);  
  85.         BTree<String> t=new BTree<String>(a);  
  86.         test(t,a,b);  
  87.     }  
  88.     static class BTNode<T>  
  89.     {  
  90.         BTNode<T> left;  
  91.         BTNode<T> right;  
  92.         T data;  
  93.         public BTNode(){}  
  94.         public BTNode(BTNode<T> l,BTNode<T> r,T d){  
  95.             this.left=l;  
  96.             this.right=r;  
  97.             this.data=d;  
  98.         }  
  99.         BTNode<T> left(BTNode<T> l){  
  100.             this.left=l;  
  101.             return this;  
  102.         }  
  103.         BTNode<T> right(BTNode<T> r){  
  104.             this.right=r;  
  105.             return this;  
  106.         }  
  107.         BTNode<T> data(T d){  
  108.             this.data=d;  
  109.             return this;  
  110.         }   
  111.     }  
  112. }  

      在没有父指针时,还可以给每个节点添加一个计数器,在进入一个节点时加1,在退出该节点时减1。访问该节点时,如果要查找的节点在该节点的子树中,则返回。实际上,这和上面的算法思想是一样的,只是实现不同。

      这两种算法的时间复杂度都是O(n),效率不错。没有父指针的情况,空间复杂度也是O(n)

0 0
原创粉丝点击