最近公共祖先LCA

双链BT

如果每个结点都有一个指针指向它的父结点,于是我们可以从任何一个结点出发,得到一个到达树根结点的单向链表。因此这个问题转换为两个单向链表的第一个公共结点(先分别遍历两个链表得到它们的长度,并求出两个长度之差。在长的链表上先遍历若干个点之后,再同步遍历两个链表,知道找到相同的结点,或者一直到链表结束)。

BST

不用递归recursive,迭代iterative就行

  1. public int query(Node t, Node u, Node v) {
  2. int left = u.value;
  3. int right = v.value;
  4. Node parent = null;
  5. //二叉查找树内,如果左结点大于右结点,不对,交换
  6. if (left > right) {
  7. int temp = left;
  8. left = right;
  9. right = temp;
  10. }
  11. while (true) {
  12. //如果t小于u、v,往t的右子树中查找
  13. if (t.value < left) {
  14. parent = t;
  15. t = t.right;
  16. //如果t大于u、v,往t的左子树中查找
  17. } else if (t.value > right) {
  18. parent = t;
  19. t = t.left;
  20. } else if (t.value == left || t.value == right) {
  21. return parent.value;
  22. } else {
  23. return t.value;
  24. }
  25. }
  26. }

普通BT

单链递归

  1. node* getLCA (node* root, node* node1, node* node2) {
  2. if(root == null)
  3. return null;
  4. if(root== node1 || root==node2)
  5. return root;
  6. node* left = getLCA(root->left, node1, node2);
  7. node* right = getLCA(root->right, node1, node2);
  8. if(left != null && right != null)
  9. return root;
  10. else if(left != null)
  11. return left;
  12. else if (right != null)
  13. return right;
  14. else
  15. return null;
  16. }

Tarjan算法

首先,Tarjan算法是一种离线算法,也就是说,它要首先读入所有的询问(求一次LCA叫做一次询问),然后并不一定按

照原来的顺序处理这些询问。而打乱这个顺序正是这个算法的巧妙之处。看完下文,你便会发现,如果偏要按原来的顺序

处理询问,Tarjan算法将无法进行。

下面是算法的详细讲解

  1. 处理结点10的时候并查集的情况
  2. (假设结点是按从左到右的顺序处理的)
  3. 10LCA10的结点集合:{10}
  4. 10LCA8结点集合:{8 9 11}
  5. 10LCA3的结点集合:{3 7}
  6. 10LCA1的结点集合:{1 2 5 6}
  7. 不属于任何集合的结点:4 12
  1.   Tarjan算法是利用并查集来实现的。它按DFS的顺序遍历整棵树。对于每个结点x,它进行以下几步操作:
  2. 计算当前结点的层号lv[x],并在并查集中建立仅包含x结点的集合,即root[x]:=x
  3. 依次处理与该结点关联的询问。
  4. 递归处理x的所有孩子。
  5. root[x]:=root[father[x]](对于根结点来说,它的父结点可以任选一个,反正这是最后一步操作了)。
  6.   现在我们来观察正在处理与x结点关联的询问时并查集的情况。由于一个结点处理完毕后,它就被归到其父结点所在的集合,所以在已经处理过的结点中(包括x本身),x结点本身构成了与xLCAx的集合,x结点的父结点及以x的所有已处理的兄弟结点为根的子树构成了与xLCAfather[x]的集合,x结点的父结点的父结点及以x的父结点的所有已处理的兄弟结点为根的子树构成了与xLCAfather[father[x]]的集合……(上面这几句话如果看着别扭,就分析一下句子成分,也可参照右面的图)假设有一个询问(x,y)(y是已处理的结点),在并查集中查到y所属集合的根是z,那么z就是xyLCAxy的路径长度就是lv[x]+lv[y]-lv[z]*2。累加所有经过的路径长度就得到答案。
  7.   现在还有一个问题:上面提到的询问(x,y)中,y是已处理过的结点。那么,如果y尚未处理怎么办?其实很简单,只要在询问列表中加入两个询问(x,y)、(y,x),那么就可以保证这两个询问有且仅有一个被处理了(暂时无法处理的那个就pass掉)。而形如(x,x)的询问则根本不必存储。
  8.   如果在并查集的实现中使用路径压缩等优化措施,一次查询的复杂度将可以认为是常数级的,整个算法也就是线性的了。
  9.   另外,实现上还有一点技巧:树的边表和询问列表的存储可以用数组模拟的链表来实现。

最近公共祖先LCA Tarjan算法

hdu2586 How far away

题意

给定一棵树,每条边都有一定的权值,q次询问,每次询问某两点间的距离。

解法

这样就可以用LCA来解,首先找到u, v 两点的lca,然后计算一下距离值就可以了。这里的计算方法是,记下根结点到任意一点的距离dis[],这样ans = dis[u] + dis[v] - 2 * dis[lca(v, v)]了,这个表达式还是比较容易理解的。。

poj1470 Closest Common Ancestors

这道题和上面那道一样,很典型的LCA问题,不过读入有点麻烦,求的是每个点被作为最近公共祖先的次数

并查集

畅通工程(HDOJ-1232)

题目描述:

某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

  

分析:

若每个城市之间有且仅有一条道路相连,那这肯定是一棵树了。边数 = 节点数 -1 ,只要我们知道城市被分成的集合数ans,要修的道路就是ans-1 ,下面贴出经过路径压缩的并查集。

  

UVA - 10608-Friends

题目大意:

给定n个人,和m个关系,m个关系代表谁和谁认识之类的,求这样的关系中,朋友圈人数最多的人数。

解题思路:

这题用并查集来判断这两个人是否属于同一个朋友圈,刚开始每个人自己形成一个朋友圈,所以每个朋友圈人数为1,然后每碰到一个关系就判断这两个人p1,p2是否属于同一个朋友圈,如果不是,就把其中一个的f[t](t=find(p1)为这个圈子的根)改为另一个朋友圈的根r=find(p2),然后把以r为根的这个圈子的人数c[r]加上以t为根的圈子的朋友数c[t]。这样最后只要找f[i] == i(这样的i是根)的这样的圈子的朋友数,找最大的c[i]。

【并查集】【树】最近公共祖先LCA-Tarjan算法的更多相关文章

  1. 最近公共祖先LCA(Tarjan算法)的思考和算法实现

    LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了f ...

  2. 最近公共祖先 LCA Tarjan算法

    来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个 ...

  3. 最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

    LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了f ...

  4. P5836 [USACO19DEC]Milk Visits S 从并查集到LCA(最近公共祖先) Tarjan算法 (初级)

    为什么以它为例,因为这个最水,LCA唯一黄题. 首先做两道并查集的练习(估计已经忘光了).简单来说并查集就是认爸爸找爸爸的算法.先根据线索理认爸爸,然后查询阶段如果发现他们的爸爸相同,那就是联通一家的 ...

  5. 最近公共祖先LCA Tarjan 离线算法

    [简介] 解决LCA问题的Tarjan算法利用并查集在一次DFS(深度优先遍历)中完成所有询问.换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法. [原理] 先来看这样一个性质:当两个节点 ...

  6. POJ 1330 LCA最近公共祖先 离线tarjan算法

    题意要求一棵树上,两个点的最近公共祖先 即LCA 现学了一下LCA-Tarjan算法,还挺好理解的,这是个离线的算法,先把询问存贮起来,在一遍dfs过程中,找到了对应的询问点,即可输出 原理用了并查集 ...

  7. 最近公共祖先 LCA 倍增算法

          树上倍增求LCA LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 ...

  8. 图论-最近公共祖先-离线Tarjan算法

    有关概念: 最近公共祖先(LCA,Lowest Common Ancestors):对于有根树T的两个结点u.v,最近公共祖先表示u和v的深度最大的共同祖先. Tarjan是求LCA的离线算法(先存储 ...

  9. 马路 树链剖分/线段树/最近公共祖先(LCA)

    题目 [问题描述] 小迟生活的城市是⼀棵树(树指的是⼀个含有 \(n\) 个节点以及 \(n-1\) 条边的⽆向连通图),节点编号从 \(1\) 到 \(n\),每条边拥有⼀个权值 \(value\) ...

  10. POJ1986 DistanceQueries 最近公共祖先LCA 离线算法Tarjan

    这道题与之前那两道模板题不同的是,路径有了权值,而且边是双向的,root已经给出来了,就是1,(这个地方如果还按之前那样来计算入度是会出错的.数据里会出现多个root...数据地址可以在poj的dis ...

随机推荐

  1. js当中的声明和初始化的顺序

    if(!("a" in window)) { var a=1; } alert(a); 这里的alert出来undefined 这句话就相当于 var a; if(!(“a” in ...

  2. 一种效率更高的for循环

    var i,array=[]; for(i=array.length;i--;) { //处理代码 } 1.for循环中使用更少的变量 2.逐步减至0,这样会更快,因为同0比较比同数组的长度比较,或同 ...

  3. 高性能javascript(记录二)

    js中有四种基本的数据存取位置.分别是:字面量.本地变量.数组元素.对象成员. 字面量:只代表自身,不存储在特定位置.js的字面量有:字符串.数字.布尔值.对象.数组.函数.正则表达式.以及特殊的nu ...

  4. onscroll

    var COUNT = 0, demo = document.getElementById('demo'); function testFn() {demo.innerHTML += 'testFN ...

  5. Uploadify使用随笔

    最近项目使用了Uploadify一个上传插件,感觉的挺好用的. 上传控件: 引用JS <script src='<% =ResolveUrl("~/JS/Uploadify/jq ...

  6. 深入理解unslider.js源码

    最近用到了一个挺好用的幻灯片插件,叫做unslider.js,就想看看怎么实现幻灯片功能,就看看源码,顺便自己也学习学习.看完之后收获很多,这里和大家分享一下. unslider.js 源码和使用教程 ...

  7. asp.net MVC code first Migrations : Model 同步到DB中

    找来找去,看来用这个功能的人很少. http://www.it165.net/pro/html/201403/10653.html 步骤: 1,在程序包管理控制台上,Enable-Migrations ...

  8. hashmap 读取

    hashTable hashSet 都差不多 以hashmap为例,底层是一个散列表 数组,然后数组存出一个entry对象,对象中有两个泛型属性,一个可以指向自身类型的引用,这样就可以在每一个数组的位 ...

  9. ScorllView中嵌套listView与Viewpager的焦点问题处理

    解决进入该页面直接显示中的listview而不是从页面最顶端开始显示在setAdapter后调用listview的smoothScrollTo(0,20); 解决listview只能显示一条 需要重写 ...

  10. spring aop 声明式事务管理

    一.声明式事务管理的概括 声明式事务(declarative transaction management)是Spring提供的对程序事务管理的方式之一. Spring的声明式事务顾名思义就是采用声明 ...