ST算法
来源:互联网 发布:数据统计公司创业 编辑:程序博客网 时间:2024/06/10 07:54
求LCA(最近公共祖先)的算法有好多,按在线和离线分为在线算法和离线算法。
离线算法有基于搜索的Tarjan算法较优,而在线算法则是基于dp的ST算法较优。
首先说一下ST算法。
这个算法是基于RMQ(区间最大最小值编号)的
而求LCA就是把树通过深搜得到一个序列,然后转化为求区间的最小编号。
比如说给出这样一棵树。
我们通过深搜可以得到这样一个序列:
节点ver 1 3 1 2 5 7 5 6 5 2 4 2 1 (先右后左)
深度R 1 2 1 2 3 4 3 4 3 2 3 2 1
首位first 1 4 2 11 5 8 6
那么我们就可以这样写深搜函数
int tot, head[N], ver[2*N], depth[2*N], first[N], dis[N];//ver:节点编号 R:深度 first:点编号位置 dir:距离void dfs(int u ,int dep){ vis[u] = true; ver[++tot] = u; first[u] = tot; depth[tot] = dep; for(int k=head[u]; k!=-1; k=e[k].next) if( !vis[e[k].v] ) { int v = e[k].v , w = e[k].w; dis[v] = dis[u] + w; dfs(v,dep+1); ver[++tot] = u; depth[tot] = dep; }
搜索得到序列之后假如我们想求4 和 7的 LCA
那么我们找4和7在序列中的位置通过first 数组查找发现在6—11
即7 5 6 5 2 4 在上面图上找发现正好是以2为根的子树。而我们只要找到其中一个深度最小的编号就可以了、
这时候我们就用到了RMQ算法。
维护一个dp数组保存其区间深度最小的下标,查找的时候返回就可以了。
比如上面我们找到深度最小的为2点,返回其编号10即可。
这部分不会的可以根据上面链接研究一些RMQ
代码可以这样写:
void ST(int n){ for(int i=1;i<=n;i++) dp[i][0] = i; for(int j=1;(1<<j)<=n;j++) { for(int i=1;i+(1<<j)-1<=n;i++) { int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1]; dp[i][j] = R[a]<R[b]?a:b; } }}//中间部分是交叉的。int RMQ(int l,int r){ int k=0; while((1<<(k+1))<=r-l+1) k++; int a = dp[l][k], b = dp[r-(1<<k)+1][k]; //保存的是编号 return R[a]<R[b]?a:b;}int LCA(int u ,int v){ int x = first[u] , y = first[v]; if(x > y) swap(x,y); int res = RMQ(x,y); return ver[res];}
以上摘自http://blog.csdn.net/y990041769/article/details/40887469
以下是整体的代码:
#include <cstdio>#include <cstring>const int MAXNODE = 100010;const int MAXEDGE = 200010;struct Edge { int u, v, dis, next; Edge() {} Edge(int u, int v, int dis, int next): u(u), v(v), dis(dis), next(next) {}};struct ST { Edge edges[MAXEDGE]; //n代表的是有多少个点,m代表的是有多少条边,tot表示的是dfs是遍历的点 int n, m, tot; //head是链表的投,depth表示的是结点的深度,first表示的是结点第一次出现的位置,ver表示的是第几个位置存的是哪个结点,dis表示的是根结点到该结点的距离 int head[MAXNODE], depth[MAXNODE], first[MAXNODE], ver[MAXNODE], dis[MAXNODE]; //dp[i][j]表示的是以i开始的,长度为2^j的区间内的最小值 int dp[MAXNODE][64]; bool vis[MAXNODE]; void init(int n) { this->n = n; m = tot = 0; memset(head, -1, sizeof(head)); memset(vis, 0, sizeof(vis)); } void AddEdge(int u, int v, int dis) { edges[m] = Edge(u, v, dis, head[u]); head[u] = m++; } //ver记录的是第tot个结点是哪个,first记录的是结点u第一次出现的位置,depth记录的是第tot个结点的深度 void dfs(int u, int dep) { vis[u] = true; ver[++tot] = u; first[u] = tot; depth[tot] = dep; for (int i = head[u]; ~i; i = edges[i].next) { int v = edges[i].v; if (!vis[v]) { //遍历完子结点后,得在标记一下,因为两个子结点之间的最近公共祖先是u dis[v] = dis[u] + edges[i].dis; dfs(v, dep + 1); ver[++tot] = u; depth[tot] = dep; } } } void RMQ() { //dp记录的是下标从i开始,长度为2的j次方这段区间内深度最小的点是ver中的第几个 for (int i = 1; i <= tot; i++) dp[i][0] = i; int a, b; for (int j = 1; (1 << j) <= tot; j++) for (int i = 1; i + (1 << j) - 1 <= tot; i++) { a = dp[i][j - 1]; b = dp[i + (1 << (j - 1))][j - 1]; if (depth[a] < depth[b]) dp[i][j] = a; else dp[i][j] = b; } } void solve(int root) { dis[root] = 0; dfs(root, 1); RMQ(); } //查询区间[x,y]中深度最小的那个数的位置 int Query(int x, int y) { int k = 0; while (1 << (k + 1) <= y - x + 1) k++; int a = dp[x][k]; int b = dp[y - (1 << k) + 1][k]; //深度最小的那个才是最近公共祖先 if (depth[a] < depth[b]) return a; return b; } //LCA查询的根据是两个点第一次出现的坐标,坐标之内的区间内,深度最小的那个点,就是他们的最近公共祖先 int LCA(int x, int y) { x = first[x]; y = first[y]; //如果没有交换一下,就会出错 // if (x > y) { x = x ^ y; y = x ^ y; x = x ^ y; } //找到该点的位置后,返回的是该点的值 int c = Query(x, y); return ver[c]; }}st;int main() { return 0;}
0 0
- ST算法
- ST算法
- ST算法
- st算法
- ST算法
- ST算法
- ST算法
- ST算法
- ST算法
- ST算法
- RMQ的ST算法
- RMQ的ST算法
- rmq-st算法
- RMQ问题ST算法
- Uva11235 RMQ ST算法
- RMQ问题 ST算法
- poj3264(ST算法)
- RMS-ST算法
- 6.设计DAO接口
- Zookeeper
- C++ 虚函数&纯虚函数&抽象类&接口&虚基类
- html5 图片预览
- Efi读取环境变量
- ST算法
- jeecms部分相关源码
- Knots 2407 (规律题)
- Android 之 自定义标签 和 自定义组件 TypedArray
- java JDK setup for linux(Ubuntu)
- POI生成Excel常见的几种样式的设置
- Android中的颜色设置
- Qt的多线程编程注意事项
- C++学习笔记 -- 虚析构函数与纯虚析构函数