LCA問題的RMQ解法解析

2021-07-23 20:28:20 字數 2012 閱讀 3985

lca問題是指最近公共祖先問題,rmq問題是只區間最小值問題,我們可以將lca問題轉化為rmq問題,然後利用rmq的解法來解決lca問題。有關rmq問題的詳解可以參考我的部落格,有關於rmq問題的詳解。

本部落格重點講如何將lca問題轉化為rmq問題。

當我們深度遍歷樹時,我們沒遇到乙個未訪問過的節點就將其存入到陣列中,同時記錄下來的還有它的深度;當我們完成了對

每乙個節點u的所有分支訪問後,會回溯到該節點u,我們也將回溯時遇到的節點存入陣列中,同時記錄下來的還有它的深度。這樣我們就能夠用陣列清楚的記錄下來深度遍歷時訪問節點的完整路徑流程。另外我們還用乙個陣列記錄好每個節點第一次訪問到的序,便於我們後續操作。

下圖為該用例樹的訪問先後次序(包括回溯時重複的)

下圖中,其中i表示次序,node[i]表示先後訪問的節點(包括回溯時的),depth[i]表示第i個訪問的節點的深度(包括回溯時訪問的)

下圖中i表示節點,r[i]表示相應的節點第一次被訪問的次序

我們可以發現,對於任意兩個節點u、v,我們根據r[u]和r[v]找到其第一次出現的次序fu、fv,可以發現,u和v的最近祖depth[fu]到depth[fv]中深度最小的那個值對應的節點(我們假設fu舉個例子,如上圖中,對於節點5和節點10,二者對應的r[5]和r[10]分別為5和14,然後我們找到depth[5]和depth[14]之間最小的深度是1,期下標值為6,然後我們根據這個下標,找到node[6]的值為3,那麼它倆的最近公共祖先就是節點3。

其c++**實現如下:

#include#include#includeusing namespace std;

vectortree[105]; //假設有105個節點的樹

int r[105]; //用於深度遍歷時每個節點第一次出現的次序

int depth[10005]; //記錄深度遍歷時經過的節點的深度的值,乙個節點課重覆記錄

int node[10005]; //記錄深度遍歷時先後經過的節點,可重覆記錄

int count=1; //記錄好次序,count從1開始

int ns; //樹中節點的個數

void inputtree() //輸入樹}/*

深度遍歷為上述幾個陣列初始化,為rmq提供資料

*/void dfs(int u,int deep)

return res;

}//計算完整的st表

void calst() }}

/********************************st演算法實現結束*********************************/

void init()

/*求節點u和節點v的最近祖先

*/int rmq(int u,int v)

int logs=(int)(log(v-u+1)/log(2)); //區間長度最大2的多少次冪

int res=st[u][logs]>st[v-gettwo(logs)+1][logs]?st[v-gettwo(logs)+1][logs]:st[u][logs];

for(int i=u;i>m;

for(int i=0;i>u>>v;

cout<

RMQ問題與LCA問題

一 區間最小 最大查詢 range minimum maximum query rmq 問題 toj 2762 描述 已知長度為l 的數列a 詢問區間 l,r 中的最值。若詢問的次數較少,可以用線性的複雜度來查詢,但如果詢問的次數過多且l 過大,那麼複雜度就會很高。所以需要更快速的查詢方法。st 演...

LCA和RMQ問題雜談

首先 mathrm 問題指的是求解樹上兩點的最近公共祖先,mathrm 問題指的是求解數列區間最值。mathrm 問題轉 mathrm 問題應該是人盡皆知了,我們可以先跑出樹的 mathrm 序,使用每次進入或回到節點都記錄一次的那種 mathrm 序,那麼只需記錄每個節點第一次出現位置就可以查詢了...

RMQ問題(ST表解法)

針對 範圍最值問題。對於陣列a 1 n 有操作如下 rmq l,r a中第l個數到第r個數中的最小值 最大值模擬 d i j 從i開始,長度為2j 的一段元素的最小值。可推 d i j min d i j 1 d i 1 j 1 j 1 查詢時,令k為滿足2k r l 1的最大整數,由於求最小值可重...