• [算法 笔记] 查找二叉树上任意两个结点的最近共同祖先(更新版本)


      1 #include <stdio.h>
      2 #include <stdlib.h> // for calloc(), free()
      3 #include <assert.h> // for assert()
      4 
      5 /* 2013.7.12
      6  * 问题:设计一个算法,找出二叉树上任意两个结点的最近共同父节点。
      7  * PS. 结点中数据均不同
      8  */
      9 typedef struct TreeNode TreeNode;
     10 typedef struct ListNode ListNode;
     11 
     12 struct TreeNode
     13 {
     14     int      m_nValue;
     15     TreeNode *m_pLeft;
     16     TreeNode *m_pRight;
     17 };
     18 
     19 #define DEBUG 0
     20 
     21 struct ListNode
     22 {
     23 #if DEBUG
     24     int         m_nValue;       // for test.
     25 #else
     26     TreeNode    *m_pData;
     27 #endif
     28     ListNode    *m_pNext;
     29 };
     30 
     31 struct ListHead
     32 {
     33     ListNode    *m_pStart;  // 指向开始结点
     34     ListNode    *m_pEnd;    // 指向末尾结点
     35 };
     36 
     37 typedef struct ListHead ListHead;
     38 
     39 // Construct lookup tree.
     40 void InsertNodeToTree( TreeNode **pStart, int value  )
     41 {
     42     if ( NULL == *pStart )
     43     {
     44         TreeNode *pCur = (TreeNode *) calloc( 1, sizeof(TreeNode) );
     45         if ( pCur == NULL )
     46         {
     47             printf( "Error for calloc.
    " );
     48             return;
     49         }
     50 
     51         pCur->m_nValue = value;
     52         pCur->m_pLeft  = NULL;
     53         pCur->m_pRight = NULL;
     54         *pStart = pCur;
     55     }
     56     else
     57     {
     58         TreeNode *pCur = *pStart;
     59 
     60         if ( pCur->m_nValue > value )
     61         {
     62             InsertNodeToTree( &pCur->m_pLeft, value );
     63         }
     64         else if ( pCur->m_nValue < value )
     65         {
     66             InsertNodeToTree( &pCur->m_pRight, value );
     67         }
     68         else
     69         {
     70             printf( "The value is in tree.
    " );
     71         }
     72     }
     73 }
     74 
     75 // Output the tree data.
     76 void InOrder( TreeNode *pStart )
     77 {
     78     if ( pStart != NULL )
     79     {
     80         if ( pStart->m_pLeft != NULL )
     81         {
     82             InOrder( pStart->m_pLeft );
     83         }
     84 
     85         printf( "%d, ", pStart->m_nValue );
     86 
     87         if ( pStart->m_pRight != NULL )
     88         {
     89             InOrder( pStart->m_pRight );
     90         }
     91     }
     92 }
     93 
     94 // Destroy the tree
     95 void DestroyTree( TreeNode **pStart )
     96 {
     97     if ( *pStart != NULL )
     98     {
     99         TreeNode *pCur = *pStart;
    100         if ( pCur->m_pLeft != NULL )
    101             DestroyTree( &pCur->m_pLeft );
    102 
    103         if ( pCur->m_pRight != NULL )
    104             DestroyTree( &pCur->m_pRight );
    105 
    106         free( pCur );
    107         pCur = NULL;
    108     }
    109 }
    110 
    111 // 正序插入结点。
    112 void CreateListHead( ListHead **pStart )
    113 {
    114     ListHead *pCur = (ListHead *)calloc( 1, sizeof(ListHead) );
    115     assert( pCur != NULL );
    116     pCur->m_pEnd    = NULL;
    117     pCur->m_pStart  = NULL;
    118     *pStart = pCur;
    119 }
    120 
    121 // Destroy list
    122 void DestroyList( ListHead **pStart )
    123 {
    124     ListNode *pCur = (*pStart)->m_pStart;
    125     ListNode *pTmp = NULL;
    126 
    127     while ( pCur != NULL )
    128     {
    129         pTmp = pCur;
    130         pCur = pCur->m_pNext;
    131         free( pTmp );
    132         pTmp = NULL;
    133     }
    134 
    135     free( *pStart );
    136     *pStart = NULL;
    137 }
    138 
    139 // Insert node to list.
    140 #if DEBUG
    141 void InsertNodeToList( ListHead *pStart, int value )    // for test
    142 #else
    143 void InsertNodeToList( ListHead *pStart, TreeNode *value )
    144 #endif
    145 
    146 {
    147     ListNode *pCur = (ListNode *) calloc( 1, sizeof(ListNode) );
    148     assert( pCur != NULL );
    149 #if DEBUG
    150     pCur->m_nValue = value; // for test
    151 #else
    152     pCur->m_pData  = value;
    153 #endif
    154     pCur->m_pNext  = NULL;
    155     if ( pStart->m_pStart == NULL )
    156     {
    157         pStart->m_pStart    = pCur;
    158         pStart->m_pEnd      = pCur;
    159     }
    160     else
    161     {
    162         // Insert node to end.
    163         pStart->m_pEnd->m_pNext = pCur;
    164         pStart->m_pEnd          = pCur;
    165     }
    166 }
    167 
    168 void OutputList( ListHead *pStart )
    169 {
    170     ListNode *pCur = pStart->m_pStart;
    171 
    172     while ( pCur != NULL )
    173     {
    174 #if DEBUG
    175         printf( "%d, ", pCur->m_nValue );   // for test
    176 #else
    177         printf( "%d, ", pCur->m_pData->m_nValue );
    178 #endif
    179         pCur = pCur->m_pNext;
    180     }
    181 }
    182 #if DEBUG
    183 void TestTree()
    184 {
    185     int arry[] = {  12, 10, 11, 9, 14, 13, 15 },
    186                  index = 0,
    187                  nlen = 0;
    188     TreeNode *pRoot = NULL;
    189 
    190     nlen = sizeof(arry) / sizeof(int);
    191     for ( ; index < nlen; ++index )
    192         InsertNodeToTree( &pRoot, arry[index] );
    193 
    194     InOrder( pRoot );
    195     DestroyTree( &pRoot );
    196 }
    197 
    198 
    199 void TestList()
    200 {
    201     int index = 0, nlen = 12;
    202     ListHead *pStart = NULL;
    203 
    204     CreateListHead( &pStart );
    205     for ( ; index < nlen; ++index )
    206     {
    207         InsertNodeToList( pStart, index );
    208     }
    209     OutputList( pStart );
    210     DestroyList( &pStart );
    211 }
    212 #endif
    213 
    214 /* 2013.7.13
    215  * 解决问题部分:
    216  */
    217 int IsInTree( TreeNode *pStart, int value, ListHead *pHead )
    218 {
    219     if ( pStart != NULL )
    220     {
    221         if ( pStart->m_nValue == value )
    222         {
    223             InsertNodeToList( pHead, pStart );
    224             return 1;
    225         }
    226 
    227         if ( pStart->m_nValue > value
    228                 && pStart->m_pLeft != NULL
    229                 && IsInTree( pStart->m_pLeft, value, pHead ) == 1)
    230         {
    231             InsertNodeToList( pHead, pStart );
    232             return 1;
    233         }
    234 
    235         if ( pStart->m_nValue < value
    236                 && pStart->m_pRight != NULL
    237                 && IsInTree( pStart->m_pRight, value, pHead ) == 1)
    238         {
    239             InsertNodeToList( pHead, pStart );
    240             return 1;
    241         }
    242     }
    243 
    244     return 0;
    245 }
    246 
    247 int LengthList( ListHead *pStart )
    248 {
    249     int cnt = 0;
    250     ListNode *pCur = pStart->m_pStart;
    251 
    252     assert( pStart != NULL );
    253 
    254     for ( ; pCur != 0; ++cnt, pCur = pCur->m_pNext );
    255 
    256     return cnt;
    257 }
    258 
    259 // 查找两个了链表第一个交点。若没有则返回NULL。
    260 TreeNode* LookForFirstIntersection( ListHead *ph1, ListHead *ph2 )
    261 {
    262     ListNode *p1Cur = ph1->m_pStart,
    263               *p2Cur = ph2->m_pStart;
    264     int nlen1 = 0,
    265         nlen2 = 0,
    266         diff = 0;
    267 
    268     assert( ph1 != NULL && ph2 != NULL );
    269     nlen1 = LengthList( ph1 );
    270     nlen2 = LengthList( ph2 );
    271 
    272     // 长的链表先走两个链表之差的长度。
    273     if ( nlen1 > nlen2 )
    274     {
    275         diff = nlen1 - nlen2;
    276         for ( ; diff > 0; --diff, p1Cur = p1Cur->m_pNext );
    277     }
    278     else
    279     {
    280         diff = nlen2 - nlen1;
    281         for ( ; diff > 0; --diff, p2Cur = p2Cur->m_pNext );
    282     }
    283 
    284     while ( p2Cur != NULL && p1Cur != NULL )
    285     {
    286         if ( p1Cur->m_pData == p2Cur->m_pData )
    287             return p1Cur->m_pData;
    288         p1Cur = p1Cur->m_pNext;
    289         p2Cur = p2Cur->m_pNext;
    290     }
    291 
    292     return NULL;
    293 }
    294 
    295 // 共同父节点。若其中一个节点是另一个结点的子节点,则输出这个节点。
    296 int RecentCommonAncestor( TreeNode *pStart, int lhs, int rhs )
    297 {
    298     ListHead *plHead = NULL,
    299               *prHead = NULL;
    300     TreeNode *pCur = NULL;
    301 
    302     CreateListHead( &plHead );
    303     CreateListHead( &prHead );
    304 
    305     if ( IsInTree( pStart, lhs, plHead ) == 1
    306             && IsInTree( pStart, rhs, prHead ) == 1 )
    307     {
    308         printf( "
    The path of lhs is 
    " );
    309         OutputList( plHead );
    310         printf( "
    The path of rhs is 
    " );
    311         OutputList( prHead );
    312 
    313         // lookup for the first node which two lists intersect.
    314         pCur = LookForFirstIntersection( plHead, prHead );
    315     }
    316 
    317     DestroyList( &plHead );
    318     DestroyList( &prHead );
    319 
    320     if ( pCur != NULL )
    321         return pCur->m_nValue;
    322     else
    323         return 0;
    324 }
    325 
    326 void TestRCA()
    327 {
    328     int arry[] = {  12, 10, 11, 9, 14, 13, 15 },
    329                  index = 0,
    330                  nlen = 0,
    331                  rhs = 9, lhs = 17;
    332     TreeNode *pRoot = NULL;
    333 
    334     nlen = sizeof(arry) / sizeof(int);
    335     for ( ; index < nlen; ++index )
    336         InsertNodeToTree( &pRoot, arry[index] );
    337 
    338     printf( "
    The data in tree is 
    " );
    339     InOrder( pRoot );
    340 
    341     index = RecentCommonAncestor( pRoot, lhs, rhs );
    342     printf( "
    The values of %d and %d ", lhs, rhs );
    343     if ( index == 0 )
    344         printf( "do not have most Recent Common Ancestor.
    " );
    345     else
    346         printf( "have most Recent Common Ancestor: %d.
    ", index );
    347 
    348     DestroyTree( &pRoot );
    349 }
    350 
    351 int main()
    352 {
    353 #if DEBUG
    354     printf( "First, we test Tree:
    
    " );
    355     TestTree();
    356 
    357     printf( "
    
    Second, we test list:
    
    " );
    358     TestList();
    359 #endif
    360 
    361     TestRCA();
    362 
    363     return 0;
    364 }
    View Code

      以前写的时候忘记将思路讲述一下,今天补充下。思路:这里要是基于树的递归结构。在遍历树的过程利用链表将查询节点所经过的路径用链表记录下来(所查节点为开始节点,根节点为末尾戒点),通过两次遍历分别得到两个路径链表。考虑到由于根节点作为链表的末尾节点,那么它们的共同最近最先节点则是两个链表的第一个交叉节点,因此这个问题也就转化为查找两个链表的第一个相交节点。

      有关如何有效的记录路径链表当在树中查找到节点时,返回true,并将节点插入到链表中。这里借助了函数调用过程中栈的使用,即当该函数没有退出时,当前局部数据还在栈中。例如,在递归过程中,当前节点的左右子树没有推出的时候,则在该层函数中,当前节点的数据还保存在系统栈中。

  • 相关阅读:
    Mysql查漏补缺
    RabbitMQ学习笔记
    memcache学习笔记
    Redis问题整理
    JedisCluster获取key所在的节点
    JavaSE编程题
    IDEA快捷键 日常整理
    Idea 常用快捷键列表
    【C++】 构造函数为什么不能声明为虚函数,析构函数可以
    【算法笔记】买卖股票问题--DP/贪心算法
  • 原文地址:https://www.cnblogs.com/life91/p/3185561.html
Copyright © 2020-2023  润新知