50-求樹中兩個結點的最低公共祖先(擴展)

一、題目描述

【題型一】輸入一個樹的兩個結點,限定該樹爲二叉搜索樹,求這兩個結點的最低公共祖先(最低父親結點)

【題型二】輸入一個樹的兩個結點,該樹爲普通樹,求這兩個結點的最低公共祖先(最低父親結點),ps:樹中每個節點有parent(三叉鏈)

【題型三】輸入一個樹的兩個結點,該樹爲普通樹,求這兩個結點的最低公共祖先(最低父親結點)限定條件:該樹不存在指向父結點的指針

二、解題思路

【題型一】

二叉搜索樹的特點是:左結點值<根結點值<右結點值
根據這個特點,已知兩個結點後,可從根結點開始遍歷樹,
1)如果當前結點的值均大於兩個結點,則最低公共祖先在當前結點的左邊,走左子樹
2)如果當前結點的值均小於兩個結點,則最低公共祖先在當前結點的右邊,走右子樹
3)如果當前結點的值在兩個結點之間(可包含兩個結點中的一個),則該結點就是最低公共祖先
如上遞歸

【題型二】

如果該樹的結點中存在指向父結點的指針,那麼這道題就轉換成了求兩個鏈表的第一個公共結點
            A
          /   \
        B     C
      /     \
    D        E
   / \      /  |  \
 F   G  H  I  J
 比如輸入的兩個結點分別是F和H,因爲它們都有指向父結點的指針,因此F可以根據父結點形成一個
 鏈表:F->D->B->A,
 同理:H->E->B->A,
 那麼F和H的最低公共祖先就是這兩個鏈表的第一個公共結點B

【題型三】

            A
          /   \
         B     C
        /   \
     D       E
   /   \    /  |  \
 F   G  H  I  J
【舉例】如上圖的樹,求樹中結點H和F的最低公共祖先,找結點H的思路如下:
1)遍歷此樹(前序遍歷),用鏈表保存到達H的遍歷路徑,例如:
    a)遍歷到A,把A放入路徑中;
    b)遍歷到B,把B放入路徑中;
    c)遍歷到D,把D放入路徑中;
    d)遍歷到F,把F放入路徑中,此時路徑爲A->B->D->F,F已經沒有子結點了,因此這條路徑不可能
    到達H,把F從結點中刪除,變成A->B->D
    e)遍歷到G,把G放入路徑中,此時路徑爲A->B->D->G,G已經沒有子結點了,因此這條路徑不可能
    到達H,把G從結點中刪除,變成A->B->D,D已經沒有子結點了,但還沒找到H,把D從路徑中刪除,
    路徑變成A->B
    f)遍歷到E,把E放入路徑中,此時路徑爲A->B->E
    h)遍歷到H,已經到達目標結點,路徑A->B->E->H就是到達H的路徑
2)遍歷此樹(前序遍歷),用鏈表保存到達F的遍歷路徑
3)找到兩條路徑後:
    a)到達H的路徑:A->B->E->H
    b)到達F的路徑:A->B->D->F
    問題變成找兩個鏈表最後一個公共結點,就是要找的最低公共祖先

三、解題算法

【題型一】二叉搜索樹中兩個結點最低公共祖先的查找

/**************************************
author:tmw
date:2018-8-14
**************************************/
#include <stdio.h>
#include <stdlib.h>

typedef struct BiTreeNode
{
    int data;
    struct BiTreeNode* lchild;
    struct BiTreeNode* rchild;
}BiTreeNode;

/**時間複雜度爲O(h),h爲樹的高度**/
BiTreeNode* findLowestCommonFather( BiTreeNode* root, BiTreeNode* node1, BiTreeNode* node2 )
{
    /**遞歸邊界**/
    if( root == NULL ) return NULL;

    if( root->data > node1->data && root->data > node2->data )
        return findLowestCommonFather(root->lchild, node1, node2);
    if( root->data < node1->data && root->data < node2->data )
        return findLowestCommonFather(root->rchild, node1, node2);
    return root;
}

【題型二】普通樹(帶有指向父結點指針)中兩個結點最低公共祖先的查找

/*************************************
author:tmw
date:2018-8-14
*************************************/
#include <stdio.h>
#include <stdlib.h>

/**假定該樹有5個結點,還有一個指向父親結點的指針**/
typedef struct TreeNode
{
    char data;
    struct TreeNode* child1;
    struct TreeNode* child2;
    struct TreeNode* child3;
    struct TreeNode* child4;
    struct TreeNode* child5;
    struct TreeNode* father;
}TreeNode;

/** findLastCommonFatherNode
 @param TreeNode* root 提供的樹
 @param TreeNode* node1 待找的第一個結點
 @param TreeNode* node2 待找的第二個結點
**/
TreeNode* findLastCommonFatherNode( TreeNode* root, TreeNode* node1, TreeNode* node2 )
{
    if( root == NULL || node1 == NULL || node2 == NULL ) return NULL;

    /**分別構建兩個結點到根節點的鏈表,並分別記錄鏈表長度**/
    TreeNode* list1 = node1;
    TreeNode* list2 = node2;
    int len1 = 0;
    int len2 = 0;

    //構建list1
    while( list1 != root )
    {
        list1 = list1->father;
        len1++;
    }
    //構建list2
    while( list2 != root )
    {
        list2 = list2->father;
        len2++;
    }

    /**轉化成找兩個鏈表的第一個公共結點**/
    int gap = abs(len1-len2);
    //當len1大於len2時,讓list1先走len1-len2步
    if( len1 > len2 )
    {
        int step = gap;
        while( step > 0 )
        {
            list1 = list1->father;
            step--;
        }
    }
    //當len2大於len1時,則list2先走len2-len1步
    if( len2 > len1 )
    {
        int step = gap;
        while( step > 0 )
        {
            list2 = list2->father;
            step--;
        }
    }
    //準備完畢後,開始找公共結點
    while( list1 != list2 )
    {
        list1 = list1->father;
        list2 = list2->father;
    }

    return list1;
}

【題型三】不含指向父結點的普通樹中,兩個結點最低公共祖先的查找

/********************************
author:tmw
date:2018-8-21
********************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct BiTreeNode
{
    int data;
    struct BiTreeNode* child1;
    struct BiTreeNode* child2;
    struct BiTreeNode* child3;
    struct BiTreeNode* child4;
}BiTreeNode;

typedef struct listNode
{
    BiTreeNode* node[100];
}listNode;

/**findNodePath 函數功能:遍歷樹,找到目標結點路徑鏈表
 @param  root          待遍歷的樹
 @param  target_node   待查找的結點
 @param  path          保存結點路徑的鏈表
**/
int i = 0;
bool findNodePath( BiTreeNode* root, BiTreeNode* target_node, listNode* path )
{
    /**遞歸出口**/
    if( root == target_node ) return true;

    /**將當前結點壓入鏈表中**/
    path->node[i++] = root;
    bool find = false;

    /**遍歷樹--有多個孩子結點**/
    if( !find && root->child1 != NULL )
        find = findNodePath(root->child1, target_node, path);
    if( !find && root->child2 != NULL )
        find = findNodePath(root->child2, target_node, path);
    /**有多少個孩子節點就遍歷多少次,這裏省略**/

    /**到最底層的葉子結點還是沒找到的話,就返回上一層,從路徑中吐出當前結點**/
    if( !find )
        i--;
    return find;
}

/** getLastCommonNode 函數功能 得到兩個路徑鏈表後,獲取兩個鏈表最後一個相同結點【兩個鏈表的頭結點相同】
 @param listNode* path1  第一個結點路徑鏈表
 @param listNode* path2  第二個結點路徑鏈表
**/
BiTreeNode* getLastCommonNode( listNode* path1, listNode* path2 )
{
    /**返回值--返回最後一個相同結點**/
    BiTreeNode* last_common_node = NULL;

    int path1_len = 0;
    int path2_len = 0;

    while( path1->node[path1_len] != NULL )
        path1_len++;
    while( path2->node[path2_len] != NULL )
        path2_len++;

    int i = 0;
    while( i < path1_len && i < path2_len )
    {
        if( path1->node[i] == path2->node[i] )
            last_common_node = path1->node[i];
        i++;
    }
    return last_common_node;
}

/** 【入口函數】 findLastCommonParentNode 給出樹結構以及要找的兩個結點,找這兩個結點的最低公共祖先
 @param BiTreeNode* root
 @param BiTreeNode* node1
 @param BiTreeNode* node2
**/
BiTreeNode* findLastCommonParentNode( BiTreeNode* root, BiTreeNode* node1, BiTreeNode* node2 )
{
    /**入參判斷**/
    if( root == NULL || node1 == NULL || node2 == NULL )
        return NULL;

    /**生成路徑鏈表**/
    listNode* path1 = (listNode*)malloc(sizeof(listNode));
    listNode* path2 = (listNode*)malloc(sizeof(listNode));
    findNodePath(root, node1, path1);
    findNodePath(root, node2, path2);

    /**找兩個鏈表的最低公共祖先**/
    return getLastCommonNode(path1, path2);
}

 

                                                                           夢想還是要有的,萬一實現了呢~~~~ヾ(◍°∇°◍)ノ゙~~~~~~~~~~~~~~~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章