另一篇比較清楚的文章:
原文:1,並查集+dfs
對整個樹進行深度優先遍歷,並在遍歷的過程中不斷地把一些目前可能查詢到的並且結果相同的節點用並查集合並.
2,分類,使每個結點都落到某個類中,到時候只要執行集合查詢,就可以知道結點的lca了。
對於乙個結點u.類別有:
以u為根的子樹、除類一以外的以f(u)為根的子樹、除前兩類以外的以f(f(u))為根的子樹、除前三類以外的以f(f(f(u)))為根的子樹……
類一的lca為u,類二為f(u),類三為f(f(u)),類四為f(f(f(u)))。這樣的分類看起來好像並不困難。
但關鍵是查詢是二維的,並沒有乙個確定的u。接下來就是這個演算法的巧妙之處了。
利用遞迴的lca過程。
當lca(u)執行完畢後,以u為根的子樹已經全部並為了乙個集合。而乙個lca的內部實際上做了的事就是對其子結點,依此呼叫lca.
當v1(第乙個子結點)被lca,正在處理v2的時候,以v1為根的子樹+u同在乙個集合裡,f(u)+編號比u小的u的兄弟的子樹 同在乙個集合裡,f(f(u)) + 編號比f(u)小的 f(u)的兄弟 的子樹 同在乙個集合裡……
而這些集合,對於v2的lca都是不同的。因此只要查詢x在哪乙個集合裡,就能知道lca(v2,x)
還有一種可能,x不在任何集合裡。當他是v2的兒子,v3,v4等子樹或編號比u大的u的兄弟的子樹(等等)時,就會發生這種情況。即還沒有被處理。還沒有處理過的怎麼辦?把乙個查詢(x1,x2)往查詢列表裡新增兩次,一次新增到x1的列表裡,一次新增到x2的列表裡,如果在做x1的時候發現 x2已經被處理了,那就接受這個詢問。(兩次中必定只有一次詢問被接受).
3,應用:
實現**:
#include#includeusing namespace std;
const int max=10001;
int f[max];
int r[max];
int indegree[max];//儲存每個節點的入度
int visit[max];
vectortree[max],qes[max];
int ancestor[max];
void init(int n)
} int find(int n)
//查詢函式,並壓縮路徑
int union(int x,int y)
else
return 1;
}//合併函式,如果屬於同一分支則返回0,成功合併返回1
void lca(int u)
//這裡可以輸入多組詢問
cin>>s>>t;
//相當於詢問兩次
qes[s].push_back(t);
qes[t].push_back(s);
for(int i=1;i<=n;i++)
} }
return 0;
}
Leetcode 最小公共祖先
演算法描述 在二叉樹中求指定兩個節點的最小公共祖先 輸入 treenode root,p,q 輸出 lca 參考思路 演算法思路 首先構建如下結構體儲存所需要的資訊 struct frame在求解過程中,需要知道當前節點以及其父節點,並記錄可能的最小公共父節點。首先構建樹的後序遍歷序列ret,以fr...
樹 20 求二叉(搜尋)樹的最小公共祖先
235.二叉搜尋樹的最近公共祖先 給定乙個二叉搜尋樹,找到該樹中兩個指定節點的最近公共祖先。例如,給定如下二叉搜尋樹 root 6,2,8,0,4,7,9,null,null,3,5 示例 1 輸入 root 6,2,8,0,4,7,9,null,null,3,5 p 2,q 8 輸出 6 解釋 節...
Leetcode 5109 最小公共區域
給你一些區域列表 regions 每個列表的第乙個區域都包含這個列表內所有其他區域。很自然地,如果區域 x 包含區域 y 那麼區域 x 比區域 y 大。給定兩個區域 region1 和 region2 找到同時包含這兩個區域的 最小 區域。如果區域列表中 r1 包含 r2 和 r3 那麼資料保證 r...