最近公共祖先(LCA)问题-在线ST算法
2017-03-24 17:20
447 查看
LCA算法详解
1. 概述
LCA(Least Common Ancestors),即最近公共祖先,是指这样一个问题:在有根树中,找出某两个结点u和v最近的公共祖先(另一种说法,离树根最远的公共祖先)。对于该问题,最容易想到的解决方案是遍历,复杂度是O(n)。但当数据量非常大且查询很频繁时,该算法也许会存在问题。2. 在线ST算法
解决此问题存在两种经典的算法,一种是在线ST算法,另外一种是离线的Tarjan算法,所谓在线算法是指用户每输入一个查询便马上处理一个查询,该算法一般用较长的时间做预处理,待信息充足以后便可以用较少的时间回答每个查询。
所谓离线算法,是指首先读入所有的询问(求一次LCA叫做一次询问),然后重新组织查询处理顺序以便得到更高效的处理方法
在线算法DFS+ST描述:将树看成一个无向图,u和v的公共祖先一定在u与v之间的最短路径上
● DFS:从树的根节点T开始,深度遍历树,并记录下每次到达的顶点。第一个的结点是root(T),每经过一条边都记录它的端点。由于每条边恰好经过2次,因此一共记录了2n-1个结点,用E[1, … , 2n-1]来表示。
● 计算R[]:用R[i]表示E数组中值为i的元素第一次出现的下标,即如果R[u] < R[v]时,DFS访问的顺序是E[R[u], R[u]+1, …, R[v]]。虽然其中包含u的后代,但深度最小的还是u与v的公共祖先。
● RMQ:当R[u] ≥ R[v]时,LCA[T, u, v] = RMQ(L, R[v], R[u]);否则LCA[T, u, v] = RMQ(L, R[u], R[v]),计算RMQ。
在线算法DFS+ST代码如下:
1.数据结构描述:// 头结点信息 private Node heads[]; // 边信息 private Edge edges[]; // 深度遍历过程中每个节点第一次出现的序号 private int first[]; // 每个节点出现的深度 private int depth[]; // 深度遍历序列 private int travel[]; // 保存节点到根节点的距离 private int dir[]; // 访问记录矩阵 private boolean vis[]; private RMQ mRmq; /** * 邻接表头结点信息 */ class Node { private int sno;// 节点编号 private Edge firstEdge; } /** * 邻接表边信息 */ class Edge { private int sno; private int from; private int to; private int wight; private Edge next; }
1.算法步骤描述:
##### 1.根据输入的节点和权重信息建立邻接表 /** * 无向图创建路径 * * @param from * @param to * @param wight */ public void createEdge(int from, int to, int wight) { addEdge(from, to, wight); addEdge(to, from, wight); } /** * 头插法创建邻接表 * * @param from * @param to * @param wight */ private void addEdge(int from, int to, int wight) { Edge edge = new Edge(); edge.from = from; edge.to = to; edge.wight = wight; edge.sno = edgeNum; edges[edgeNum++] = edge; edge.next = heads[from].firstEdge; heads[from].sno = from; heads[from].firstEdge = edge; } ##### 2.深度优先遍历,计算访问序列、深度信息、节点第一次出现的位置信息等 /** * 深度遍历邻接表 * * @param u * @param dep */ public void travelInDepth(int u, int dep) { vis[u] = true; travel[++index] = u; first[u] = index; depth[index] = dep; for (Edge edge = heads[u].firstEdge; edge != null; edge = edge.next) { if (!vis[edge.to]) { int v = edge.to; dir[v] = dir[u] + edge.wight; travelInDepth(v, dep + 1); travel[++index] = u; depth[index] = dep; } } } ##### 3.根据输入的查询,回答结果 /** * 回答节点的最近公共祖先节点 * @param u * @param v * @return */ public int getLCANode(int u, int v) { if (mRmq == null) { mRmq = new RMQ(); mRmq.RMQInit(depth); } u = first[u]; v = first[v]; if (u < v) { return mRmq.getMax(u, v); } return mRmq.getMax(v, u); }
相关文章推荐
- 最近公共祖先(LCA)和RMQ问题
- LCA(最近公共祖先)问题的离线算法
- 最近公共祖先LCA问题
- 1 数据结构类-最近公共祖先LCA问题
- 二叉搜索树(BST)的最近公共祖先(LCA)问题(Lowest Common Ancestor of a Binary Tree)
- LCA问题(最近公共祖先问题)+ RMQ问题
- 最近公共祖先LCA问题
- LCA问题——最近公共祖先(Least Common Ancestors)
- 【Leetcode】查找二叉树中任意结点的最近公共祖先(LCA问题)
- Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)
- 【Leetcode】查找二叉树中任意结点的最近公共祖先(LCA问题)
- 第三十九章:最近公共祖先LCA问题
- LCA最近公共祖先的离线算法(Tarjan)和在线算法(ST)
- LCA问题(最近公共祖先问题)+ RMQ问题
- 最近公共祖先问题(LCA)
- POJ1330Nearest Common Ancestors最近公共祖先LCA问题
- LCA(最近公共祖先)问题
- 【LCA】最近公共祖先问题Lowest Common Ancestors
- LCA问题:求二叉树中任意两个节点的最近公共祖先
- LCA(最近公共祖先)问题的离线算法