節點的最近公共祖先 倍增演算法

2021-08-31 16:04:57 字數 2544 閱讀 7394

## **題目描述**:

這是乙個裸的lca問題,即求書上兩個節點的最近公共祖先。

我們可以用樹上倍增來做;

當然,在做之前我們假設不知道該演算法。那麼我們如何來做這種型別的題目呢?

顯然,我們可以用暴力來做,找到兩點的最近公共祖先,我們可以用前向星存雙向邊,然後依次儲存每個點到的根的路徑。然後找到最先同時出現在兩條路徑的公共點即可;

顯然,這樣是可做的。但是這樣做,考慮到:

1.當書的深度非常大

2.當最壞情況樹退化為一條鏈式

3.當詢問次數非常多的時候。

以上,無論那種情況,起步都是o(n^2)的複雜度,顯然,這樣的做法並不夠優秀,所以我們引入倍增的做法,它可以nlogn的時間複雜度完成預處理,每次詢問logn的時間給出回答;

倍增演算法:所謂倍增,就是按2的倍數來增大,也就是跳 1,2,4,8,16,32 …… 不過在這我們不是按從小到大跳,而是從大向小跳,即按……32,16,8,4,2,1來跳,如果大的跳不過去,再把它調小。這是因為從小開始跳,可能會出現「悔棋」的現象。拿 5為例,從小向大跳,5≠1+2+4,所以我們還要回溯一步,然後才能得出5=1+4;而從大向小跳,直接可以得出5=4+1。這也可以拿二進位制為例,5(101),從高位向低位填很簡單,如果填了這位之後比原數大了,那我就不填,這個過程是很好操作的。

lca的倍增做法就是 用乙個fa[i][j]陣列 來儲存 i 節點的 2^j 倍父親是哪個節點; 這樣做每次都是2倍區間查詢,所以可以在logn的時間給出答案;

我們先跑一遍dfs, 初始化 深度 d陣列 , 初始化 fa[i][0] 。 跑一遍dfs後,d陣列儲存的就是每個節點的深度(預設根節點深度為1). fa陣列儲存的就是 fa[i][0] 就是 每個節點儲存的都是他父親節點的編號;

跑完dfs,我們需要對全域性fa進行賦值; 這裡用到的轉移方程是

fa[i][j] = fa[fa[i][j-1]][j-1] 。

比如(畫的比較醜)

2節點的 fa[2][0] 為 1 。 就是 2節點 的 (2^0 = 1)的父親為 1

4節點的fa[4][0]為 2。 就是 4號節點的父親為 2

要求 fa4 我們可以表示為 fa[fa[4][0]][0], 就是4號父親的父親, 這樣遞推上去,就可以的出每個fa;

完成這步之後,我們剩下就是求lca了;

先把x節點和y節點挪到同一層, 挪到同一層後, 若 x == y 代表一開始 x 就是y的祖先或者y是x的祖先;

若 x != y, 則 我們找到最深的滿足 fa[x][i] != fa[y][i] ;

然後返回x的父親節點即可。

至此,我們的演算法就結束了;

lca倍增學習:很明白的lca教程

1.建樹用前向星建, 注意邊數為n-1條,雙向邊,則應該開闢max_n * 2空間

2.這道題指明a是b的父親,得用並查集維護一下根節點,不然會wa的

#include

using namespace std;

#define max_n 10010

int head[max_n]

, cnt =

0, d[max_n]

, fa[max_n][30

];struct nodeedge[max_n *2]

;void

add(

int x,

int y)

void

dfs(

int k)}}

intlca

(int x,

int y)

for(

int i =

20; i>=

0; i--)}

if(x == y)

return x;

for(

int i =

20; i>=

0; i--)}

return fa[x][0

];}int pre[max_n]

;void

init

(int n)

intfind

(int x)

void

union

(int x,

int y)

}int

main()

for(

int i =

1; i

)int r =

find

(pre[1]

);fa[r][0

]=0, d[r]=1

;dfs

(r);

for(

int i =

1; i<=

20; i++)}

int t;

cin >> t;

while

(t--

)return0;

}

最近公共祖先 倍增演算法

模板題是這個樣子的 給你一顆有根樹,每次查詢兩個節點的最近公共祖先。最近公共祖先為何物?簡單來說,就是兩點間的路徑中深度最小的那個節點。那麼,有什麼辦法可以求最近公共祖先呢?最樸素的辦法 首先讓兩個節點中深度大的節點往上乙個乙個節點地跳,直到兩個節點深度一樣,然後一起跳,直到他們跳到同乙個點。顯然,...

LCA 最近公共祖先 (倍增演算法)

首先了解一下我們 最近公共祖先 e和g的lca為a l和j的lca為d k和f的lca為b 然後 倍增 用到了二進位制和 dp 的思想 倍增 就是 1 2 4 8 16 任何乙個數 都是可以右 這些數相加得到的。了解一下二進位制 首先 定義 fa i j 為 從 i 節點 向上走 2 j 個節點,d...

最近公共祖先 LCA 倍增演算法

樹上倍增求lca lca指的是最近公共祖先 least common ancestors 如下圖所示 4和5的lca就是2 那怎麼求呢?最粗暴的方法就是先dfs一次,處理出每個點的深度 然後把深度更深的那乙個點 4 乙個點地乙個點地往上跳,直到到某個點 3 和另外那個點 5 的深度一樣 然後兩個點一...