最近公共祖先 tarjan离线算法 C++
最近做到一道题目,大概的意思就是求一个多叉树中两个节点的最近公共祖先,输入是用邻接矩阵表示的。
要想理解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算法,得先理解并查集
posted on 2015-04-26 14:18 Natsukashiii 阅读(1159) 评论(0) 编辑 收藏 举报