樹如何找共同祖先 尋找二叉樹兩個節點的最低公共祖先

2021-10-12 13:23:01 字數 2949 閱讀 7895

給定一棵樹,同時給出樹中的兩個結點(n1和n2),求它們的最低公共祖先。也就是常見的lca(lowest common ancestor )問題。

看下面的圖就明白了:

方法一下面是乙個簡單的複雜度為 o(n) 的演算法,解決lca問題

1)找到從根到n1的路徑,並儲存在乙個向量或陣列中。

2)找到從根到n2的路徑,並儲存在乙個向量或陣列中。

3) 遍歷這兩條路徑,直到遇到乙個不同的節點,則前面的那個即為最低公共祖先.

下面的c++的程式實現

// o(n) 解決 lca

#include

#include

using namespace std;

//二叉樹節點

struct node

int key;

struct node *left, *right;

//公用函式,生成乙個節點

node * newnode(int k)

node *temp =new node;

temp->key = k;

temp->left = temp->right = null;

return temp;

//找到從root到 節點值為key的路徑,儲存在path中。沒有的話返回-1

bool findpath(node * root,vector &path,int key)else

ans = path1[i];

return ans;

return -1;

// driver program to test above functions

int main()

// 按照上面的圖來創建立樹

node * root = newnode(1);

root->left = newnode(2);

root->right = newnode(3);

root->left->left = newnode(4);

root->left->right = newnode(5);

root->right->left = newnode(6);

root->right->right = newnode(7);

cout <

cout <

cout <

cout <

return 0;

輸出:lca(4, 5) = 2

lca(4, 6) = 1

lca(3, 4) = 1

lca(2, 4) = 2

時間複雜度:o(n), 樹被遍歷了兩次,每次遍歷複雜度不超過n,然後比較路徑。

第二種方法(只遍歷一次)

上面的方法雖然是o(n),但是操作依然繁瑣了一點,並且需要額外的空間來儲存路徑。其實可以只遍歷一次,利用遞迴的巧妙之處。學好二叉樹,其實就是學好遞迴。

從root開始遍歷,如果n1和n2中的任乙個和root匹配,那麼root就是lca。 如果都不匹配,則分別遞迴左、右子樹,如果有乙個 key(n1或n2)出現在左子樹,並且另乙個key(n1或n2)出現在右子樹,則root就是lca.  如果兩個key都出現在左子樹,則說明lca在左子樹中,否則在右子樹。

/* 只用一次遍歷解決lca */

#include

using namespace std;

struct node

struct node *left, *right;

int key;

node* newnode(int key)

node *temp =new node;

temp->key = key;

temp->left = temp->right = null;

return temp;

// 返回n1和n2的 lca的指標

// 假設n1和n2都出現在樹中

struct node *findlca(struct node* root,int n1,int n2)

if (root == null)return null;

// 只要n1 或 n2 的任乙個匹配即可

//  (注意:如果 乙個節點是另乙個祖先,則返回的是祖先節點。因為遞迴是要返回到祖先的 )

if (root->key == n1 || root->key == n2)

return root;

// 分別在左右子樹查詢

node *left_lca  = findlca(root->left, n1, n2);

node *right_lca = findlca(root->right, n1, n2);

// 如果都返回非空指標 non-null, 則說明兩個節點分別出現了在兩個子樹中,則當前節點肯定為lca

if (left_lca && right_lca)return root;

// 如果乙個為空,在說明lca在另乙個子樹

return (left_lca != null)? left_lca: right_lca;

//測試

int main()

// 構造上面圖中的樹

node * root = newnode(1);

root->left = newnode(2);

root->right = newnode(3);

root->left->left = newnode(4);

root->left->right = newnode(5);

root->right->left = newnode(6);

root->right->right = newnode(7);

cout cout cout cout return 0;

時間複雜度為o(n),但是上面的方法還是有所侷限的,必須保證兩個要查詢的節點n1和n2都出現在樹中。如果n1不在樹中,則會返回n2為lca,理想答案應該為null。要解決這個問題,可以先查詢下 n1和n2是否出現在樹中,然後加幾個判斷即可。

二叉樹最近共同祖先

從鍵盤接收擴充套件先序序列,以二叉鍊錶作為儲存結構,建立二叉樹。求兩個不同結點ch1,ch2的最近共同祖先。第一行 擴充套件先序序列 第二行 ch1,ch2兩個不同結點值,用乙個空格間隔。abc de g f c fb include include include typedef struct n...

尋找二叉樹兩個結點的最低共同父節點

題目 二叉樹的結點的定義如下 struct treenode 輸入二叉樹中的兩個結點,輸出這兩個結點在數中最低的共同父結點。尋找二叉樹兩個結點的最低共同父節點 treenode findfirstcommonparentnode treenode proot,treenode pnodeone,tr...

編號滿二叉樹 尋找任意兩結點的最近共同祖先

這是第一篇 技術性 部落格,希望以後越寫越多越好咯,本科生的期許hhh 這道題運用的主要性質就是對於滿二叉樹任意結點編號i 其parent 若有 為 i 2 關鍵在於對演算法的優化 一開始只想把 寫出來,於是用了 math.h 的pow函式,意圖先算出兩個結點的level,然後根據level 的差值...