posts - 403, comments - 310, trackbacks - 0, articles - 7
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          LCA 最近公共祖先問題 (1)

          Posted on 2007-07-14 10:57 ZelluX 閱讀(2828) 評論(2)  編輯  收藏 所屬分類: Algorithm
          LCA (Lowest Common Ancestor)
          一篇轉載的文章,是對于二叉搜索樹的算法
          http://blog.csdn.net/AmiRural/archive/2006/06/07/777966.aspx

          [題目]:已知二元搜索樹(Binary Search Tree)上兩個結點的值,請找出它們的公共祖先。你可以假設這兩個值肯定存在。這個函數的調用接口如下所示:
                int FindLowestCommonAncestor(node *root, int value1, int value2);

              根據樹的存儲結構,我們可以立刻得到一個這樣的算法:從兩個給定的結點出發回溯,兩條回溯路線的交點就是我們要找的東西。這個算法的具體實現辦法是:從根 結點開始,先用這兩個結點的全體祖先分別生成兩個鏈表,再把這兩個鏈表第一次出現不同結點的位置找出來,則它們的前一個結點就是我們要找的東西。
              這個算法倒沒有什么不好的地方,但是它沒有利用二元搜索樹的任何特征,其他類型的樹也可以用這個算法來處理。二元搜索樹中左結點的值永遠小于或者等于當前 結點的值,而右結點的值永遠大于或者等于當前結點的值。仔細研究,4和14的最低公共祖先是8,它與4和14的其他公共祖先是有重要區別的:其他的公共祖 先或者同時大于4和4,或者同時小于4和14,只有8介于4和14之間。利用這一研究成果,我們就能得到一個更好的算法。
              從根結點出發,沿著兩個給定結點的公共祖先前進。當這兩個結點的值同時小于當前結點的值時,沿當前結點的左指針前進;當這兩個結點的值同時大于當前結點的 值時,沿當前結點的右指針前進;當第一次遇到當前結點的值介于兩個給定的結點值之間的情況時,這個當前結點就是我們要找的最的最低公共祖先了。
              這是一道與樹有關的試題,算法也有遞歸的味道,用遞歸來實現這一解決方案似乎是順理成章的事,可這里沒有這個必要。遞歸技術特別適合于對樹的多個層次進行 遍歷或者需要尋找某個特殊結點的場合。這道題只是沿著樹結點逐層向下前進,用循環語句來實現有關的過程將更簡單明了。

               int FindLowestCommonAncestor(node *root, int value1, int value2)
               {
                node 
          *curnode = root;
                
          while(1)
                   {
                     
          if (curnode->data>value1&&curnode->data>value2)
                       curnode 
          = curnode->left;
                     
          else if(curnode->data<value1&&curnode->data<value2)
                       curnode 
          = curnode->right;
                     
          else
                       
          return curnode->data;
                    }
                }

          C語言實現:

          /*二叉排序樹的生成及樹,任意兩結點的最低公共祖先        Amirural設計*/
          #include 
          <stdio.h>
          #define  null  0

          int counter=0;
          typedef 
          struct btreenode
          {
          int data;
           
          struct btreenode *lchild;
           
          struct btreenode *rchild;
          }bnode;

          bnode 
          *creat(int x,bnode *lbt,bnode *rbt)   //生成一棵以x為結點,以lbt和rbt為左右子樹的二叉樹
          {bnode *p;
           p
          =(bnode*)malloc(sizeof(bnode));
           p
          ->data=x;
           p
          ->lchild=lbt;
           p
          ->rchild=rbt;
           
          return(p);
          }

          bnode 
          *ins_lchild(bnode *p, int x)    //x作為左孩子插到二叉樹中
          {bnode *q;
          if(p==null)
            printf(
          "Illegal insert.");
          else
           {q
          =(bnode*)malloc(sizeof(bnode));
            q
          ->data=x;
            q
          ->lchild=null;
            q
          ->rchild=null;
            
          if(p->lchild!=null)                //若p有左孩子,則將原來的左孩子作為結點x的右孩子
               q->rchild=p->lchild;
            p
          ->lchild=q;
           }
           
          return(p);
          }

          bnode 
          *ins_rchild(bnode *p, int x)   //x作為右孩子插入到二叉樹
          {bnode *q;
           
          if(p==null)
              printf(
          "Illegal insert.");
           
          else
           {q
          =(bnode*)malloc(sizeof(bnode));
            q
          ->data=x;
            q
          ->lchild=null;
            q
          ->rchild=null;
            
          if(p->rchild!=null)                //若x有右孩子,則將原來的右孩子作為結點x的的左孩子
               q->lchild=p->rchild;
            p
          ->rchild=q;
           }
            
          return(p);
          }

          void prorder(bnode *p)
          {
          if(p==null)
             
          return;
           printf(
          "%d\t%u\t%d\t%u\t%u\n",++counter,p,p->data,p->lchild,p->rchild);
           
          if(p->lchild!=null)
             prorder(p
          ->lchild);
           
          if(p->rchild!=null)
             prorder(p
          ->rchild);
          }

          void print(bnode *p)                //嵌套括號表示二叉樹,輸出左子樹前打印左括號,
          {                                   //輸出右子樹后打印右括號。
            if(p!=null)
            {printf(
          "%d",p->data);
             
          if(p->lchild!=null||p->rchild!=null)
             {printf(
          "(");
            print(p
          ->lchild);
            
          if(p->rchild!=null)
             printf(
          ",");
            print(p
          ->rchild);
            printf(
          ")");
           }
          }
          }

          int FindLowestCommonAncestor(bnode *root, int value1, int value2)
          {
            bnode 
          *curnode = root;
            
          while(1)
            {
             
          if (curnode->data>value1&&curnode->data>value2)
                 curnode 
          = curnode->lchild;
             
          else if(curnode->data<value1&&curnode->data<value2)
                 curnode 
          = curnode->rchild;
             
          else
                 
          return curnode->data;
             }
          }

           
          main()
          {
           bnode 
          *bt,*p,*q;
           
          int x,y,v1,v2;
           printf(
          "輸入根結點:");
           scanf(
          "%d",&x);
           p
          =creat(x,null,null);
           bt
          =p;                                   //使bt p都指向根結點
           printf("輸入新的結點值:");
           scanf(
          "%d",&x);
           
          while(x!=-1)
             {p
          =bt;
              q
          =p;                    
           
          while(x!=p->data&&q!=null)              //q記錄當前根結點
             {p=q;
              
          if(x<p->data)
              q
          =p->lchild;
              
          else
              q
          =p->rchild;
             }
            
          if(x==p->data)
            {printf(
          "元素%d已經插入二叉樹中!\n",x);
            }
            
          else
             
          if(x<p->data)    ins_lchild(p,x);
                
          else             ins_rchild(p,x);
             scanf(
          "%d",&x);
             }
           p
          =bt;
           printf(
          "struct of the binary tree:\n");
           printf(
          "number\taddress\tdata\tlchild\trchild\n");
           prorder(p);
           printf(
          "\n");
           printf(
          "用括號形式輸出二叉樹:");
           print(p);

           printf(
          "\n請任意輸入樹中存在的兩個結點:");
           scanf(
          "%d,%d",&v1,&v2);
           y 
          = FindLowestCommonAncestor(p, v1, v2);
           printf(
          "輸出%d和%d的最低公共祖先:",v1,v2);
           printf(
          "%d\n",y);
          }

           

           運行結果:
          輸入根結點:20
          輸入新的結點值:8 22 4 12 10 14 -1       (以-1結束結點的輸入)
          struct of the binary tree:
          number   addresss      data      lchild         rchild
          1               4391168       20        4391104   4391040
          2               4391104       8          4390976   4399072
          3               4390976      4           0                 0
          4               4399072     12          4399008  4398944
          5               4399008     10          0                0
          6               4398644     14          0                0
          7               4391040     22          0                0
          用括號形式輸出:20(8(4,12(10,14)),22)   (輸出左子樹打印左括號,輸出右子樹后打印括號)
          請任意輸入樹中存在的兩個結點:4,14
          輸出4和14的最低祖先:8




          評論

          # re: LCA 最近公共祖先問題 (1)  回復  更多評論   

          2007-10-06 17:15 by EvilGenuis
          很好,很強大 !

          # re: LCA 最近公共祖先問題 (1)  回復  更多評論   

          2008-11-09 22:03 by lonelycorn
          為什么不用tarjan算法?
          主站蜘蛛池模板: 二连浩特市| 靖边县| 鄂伦春自治旗| 大安市| 萨嘎县| 黔江区| 竹北市| 盘山县| 焉耆| 逊克县| 乐陵市| 特克斯县| 西贡区| 府谷县| 垦利县| 休宁县| 彭水| 榆中县| 老河口市| SHOW| 阳西县| 抚顺县| 乌兰县| 汤原县| 革吉县| 玉树县| 康保县| 宿迁市| 常山县| 汝南县| 公安县| 吉水县| 光山县| 宜黄县| 明水县| 香格里拉县| 台东市| 溆浦县| 永康市| 盐池县| 顺昌县|