一、題目描述
【題型一】輸入一個樹的兩個結點,限定該樹爲二叉搜索樹,求這兩個結點的最低公共祖先(最低父親結點)
【題型二】輸入一個樹的兩個結點,該樹爲普通樹,求這兩個結點的最低公共祖先(最低父親結點),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);
}
夢想還是要有的,萬一實現了呢~~~~ヾ(◍°∇°◍)ノ゙~~~~~~~~~~~~~~~~~