標籤:
最近做到一道題目,大概的意思就是求一個多叉樹中兩個節點的最近公用祖先,輸入是用鄰接矩陣表示的。
要想理解tarjan演算法並實現它,需要先理解一下內容:
1) 深度優先搜尋;tarjan演算法核心思想:當某節點剛剛搜尋完畢時,看與其相關的結點v是否已經被訪問,如果v已經被訪問過了,則它們的最近公用祖先就是v的祖先。
2) 並查集原理和實現方法,並查集的代表和祖先的區別(其實也可以一起表示),祖先的更新時刻
3) 如何表示多叉數(鄰接鏈表,鄰接矩陣),如何表示查詢對,如何記錄查詢結果
下面是c++實現代碼,比較偷懶,每次調用函數就查詢一個。查詢對的資料結構可以用下面提到的鄰接表來表示。
#include <vector>#include <string>#include <algorithm>#include <iostream>using namespace std;class lca{public: lca(const vector<string> &vstr) :str(vstr),f(vstr.size(),-1),ancestor(vstr.size(),-1),visit(vstr.size(),0) { } int lca_query(int vertex,int u, int v){ static int ans=-1; ancestor[vertex] = vertex; visit[vertex] = true; for (int i = 0; i < str.size(); ++i){ if ((str[vertex][i] == ‘1‘) && (visit[i] == 0)){ ans=lca_query(i, u, v); unite(vertex, i); ancestor[find(vertex)] = vertex; } } if (vertex == u&&visit[v]) ans = ancestor[find(v)]; if (vertex == v&&visit[u]) ans = ancestor[find(u)]; return ans; }private: const vector<string> &str; vector<int> f; //並查集 vector<int> ancestor; vector<bool> visit; int find(int i) { if (f[i] == -1) return i; return f[i] = find(f[i]); } void unite( int u, int v) { int x = find(u); int y = find(v); if (x != y) f[x] = y; }};int main(){ vector<string> v123 = { "01100001", "10011000", "10000000", "01000000", "01000110","00001000","00000100","10000000" }; lca query1(v123); cout << query1.lca_query(0,4, 7);}
實際上用鄰接矩陣來表示多叉數是很浪費時間的,單顆多叉樹作為無環圖,只有(n-1)條邊,這裡n是節點數。網上有很好的方法來表示鄰接鏈表,
struct Edge{ int to, next;}edge[maxn * 2];int head[maxn], tot;void addedge(int u, int v)//鄰接表頭插法加邊{ edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++;}
本質上就是一個單鏈表,不過這個單鏈表的next指標只是一個數組下標值。head[u]記錄的是上一次從u出發的邊的數組下邊。
要理解tarjan演算法,得先理解並查集
最近公用祖先 tarjan離線演算法 C++