tarjan算法解决LCA问题
来源:互联网 发布:用c语言制作病毒 编辑:程序博客网 时间:2024/06/06 05:24
昨天学习了tarjan算法解决LCA问题,今天来总结一下。
首先,tarjan算法需要并查集的相关知识,你可以参考:并查集详解 (转)。然后可以简单应用并查集:并查集及其在最小生成树中的应用。
并查集最主要两点:路径压缩和按秩合并。前者在find函数中实现,后者在union函数中实现。后者意思是说我们在union操作中,总是要把节点数目少的树作为结点数目多的树的子树。
在求最小生成树的克鲁斯卡尔算法中,我们先对每条边排序。然后按照由小到大的顺序依次将每条边并入并查集,如果并入成功,这就是找到的最小生成树的又一边。否则,说明加入该边后存在环路,则取消并入。依次下去,就得到了最小生成树。
然后回到主题tarjan算法。你可以参考:LCA问题的Tarjan算法。如果不懂,这有对该博客的解释:并查集实现Tarjan算法。更详细的步骤演算这篇博客中有推理:LCA 最近公共祖先。
好了,我来说说tarjan算法的关键就行了。tarjan算法的关键就在于ancestor数组。该数组用来存储每个节点的祖先。注意,我们在tarjan算法中用到了并查集,但是并查集中每个集合的最终father并不是所谓的祖先,两个不是同一个东西!并查集中的father是一个叶子节点,为什么这么做呢?因为我们在更新中间节点的祖先时,采用ancestor[find(x)]=x操作,如果中间节点的祖先是它的叶子节点,那么find(x)操作会一直向下把直到叶子节点(它是最终father)上路径上所有节点的祖先都顺道成功更新了了!某个节点x的子树上所有节点的ancestor不正是x吗?只是因为x在DFS中,是后遍历并且并入并查集的节点,这个巧妙地操作就完成了x的子树上所有祖先ancestor的更新。
我的实现代码如下:
#include <iostream>#include <vector>using namespace std;const int MAXN = 1001;vector<int> tree[MAXN];int indegree[MAXN], ancestor[MAXN];int nvertex, root;int father[MAXN], rnk[MAXN];bool visited[MAXN];int nquery;vector<int> query[MAXN];void init(){ for(int i=0; i<nvertex; ++i){ tree[i].clear(); indegree[i] = 0; ancestor[i] = i; father[i] = i; rnk[i] = 0; visited[i] = false; query[i].clear(); }}/*int find(int x){ int rt = x; while(rt != father[rt]) rt = father[rt]; while(father[x] != rt){ int y = father[x]; father[x] = rt; x = y; } return rt;}*/int find(int x){ if(x != father[x]) father[x] = find(father[x]); return father[x];}void unin(int x, int y){ x = find(x), y = find(y); if(x == y) return ; if(rnk[x] > rnk[y]) father[y] = x; else father[x] = y, rnk[y] += rnk[x] == rnk[y];}void tarjan(int x){ for(int i=0; i<tree[x].size(); ++i){ tarjan(tree[x][i]); unin(x, tree[x][i]); ancestor[find(x)] = x; } visited[x] = true; for(int i=0; i<query[x].size(); ++i){ if(visited[query[x][i]]) printf("the LCA for %d and %d is %d\n", x, query[x][i], ancestor[find(query[x][i])]); }}int main(){ scanf("%d", &nvertex); init(); int x, y; for(int i=1; i<nvertex; ++i){ scanf("%d%d", &x, &y); tree[x].push_back(y); //x->y indegree[y]++; } scanf("%d", &nquery); for(int i=0; i<nquery; ++i){ scanf("%d%d", &x, &y); query[x].push_back(y); query[y].push_back(x); } for(int i=0; i<nvertex; ++i) if(indegree[i] == 0) { root = i; break; } tarjan(root); return 0;}
测试用图:
输入:
8 0 1 0 2 0 3 1 4 1 5 5 7 3 6 7 1 4 4 5 4 7 5 7 0 5 4 3 1 6
输出(我的输出结果不是这个,但输出是正确的,我没有截图):
7和4的最近公共祖先为:1 5和4的最近公共祖先为:1 5和7的最近公共祖先为:5 1和4的最近公共祖先为:1 6和1的最近公共祖先为:0 3和4的最近公共祖先为:0 0和5的最近公共祖先为:0
另外,如果要熟悉并查集,我这里还有两道算法题: 200. Number of Islands+130. Surrounded Regions(并查集/DFS)。
- tarjan算法解决LCA问题
- LCA 问题 Tarjan算法
- LCA问题的Tarjan算法
- 【算法笔记】LCA问题-tarjan 离线算法
- LCA问题的Tarjan算法(POJ1330)
- LCA 问题 用 Tarjan 离线算法 求解
- (算法)Tarjan离线算法解决LCA问题 (附POJ 1470 Closest Common Ancestors 代码)
- (算法)Tarjan离线算法解决LCA问题 (附POJ 1470 Closest Common Ancestors 代码)
- LCA 离线算法 tarjan
- lca---tarjan算法
- LCA&&Tarjan算法
- LCA的Tarjan算法
- LCA 离线tarjan算法
- LCA Tarjan离线算法
- LCA离线算法tarjan
- LCA---Tarjan算法
- LCA Tarjan算法理解
- LCA离线算法Tarjan
- 迷之好奇 —静态字典树
- java annotation使用介绍
- GDKOI-2004-题解
- CSU-1100
- Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation
- tarjan算法解决LCA问题
- (三)创建型模式--抽象工厂模式
- Java设计模式-单例模式
- #UVA1635#Irrelevant Elements
- P1036 选数
- 解决WEB-INF/lib目录下的jar包无法用maven打包
- 监控impdp/expdp的进度方法
- leetcode--111. Minimum Depth of Binary Tree
- poj专题 - 初期数学