Lca相關演算法
Lca相關演算法
簡介
在圖論和電腦科學中,最近公共祖先是指在一個樹或者有向無環圖中同時擁有v和w作為後代的最深的節點。在這裡,我們定義一個節點也是其自己的後代,因此如果v是w的後代,那麼w就是v和w的最近公共祖先。
最近公共祖先是兩個節點所有公共祖先中離根節點最遠的,計算最近公共祖先和根節點的長度往往是有用的。比如為了計算樹中兩個節點v和w之間的距離,可以使用以下方法:分別計算由v到根節點和w到根節點的距離,兩者之和減去最近公共祖先到根節點的距離的兩倍即可得到v到w的距離。
求解方法
線上演算法ST表
線上演算法和離線演算法:
在電腦科學中,一個線上演算法是指它可以以序列化的方式一個個的處理輸入,也就是說在開始時並不需要已經知道所有的輸入。相對的,對於一個離線演算法,在開始時就需要知道問題的所有輸入資料,而且在解決一個問題後就要立即輸出結果。例如,選擇排序在排序前就需要知道所有待排序元素,然而插入排序就不必。
首先我們們對於以下一棵樹
分析如下
在轉化成區間之後,我們可以使用RMQ相關演算法來解決該問題。
對於dfs演算法:
int tot,head[N],ver[2*N],R[2*N],first[N],dir[N];
//ver:節點編號 R:深度 first:點編號第一次位置 dir:距離
void dfs(int u ,int dep)
{
vis[u] = true;
ver[++tot] = u;
first[u] = tot;
R[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;
dir[v] = dir[u] + w;
dfs(v,dep+1);
//下面兩句表示dfs的時候還要回溯到上面
ver[++tot] = u;
R[tot] = dep;
}
}
假設我們需要得到D和F的LCA,
那麼我們找D和F在序列中的位置可以通過first陣列發現在陣列下標為3—–6,在該區間中找到深度為2,該點的編號為4,即為B點(得出區間後,我們使用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];
}
Tarjan離線演算法
tarjan演算法的有點在於相對穩定,時間複雜度也比較居中,比較容易理解
基本思路是:
1.任選一個點為根節點,從根節點開始。
2.遍歷該點u所有子節點v,並標記這些子節點v已被訪問過。
3.若是v還有子節點,返回2,否則下一步。
4.合併v到u上。
5.尋找與當前點u有詢問關係的點v。
6.若是v已經被訪問過了,則可以確認u和v的最近公共祖先為v被合併到的父親節點a。
虛擬碼如下:
Tarjan(u)//marge和find為並查集合並函式和查詢函式
{
for each(u,v) //訪問所有u子節點v
{
Tarjan(v); //繼續往下遍歷
marge(u,v); //合併v到u上
標記v被訪問過;
}
for each(u,e) //訪問所有和u有詢問關係的e
{
如果e被訪問過;
u,e的最近公共祖先為find(e);
}
}
完整程式碼:
#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;
}
- Lca介紹https://zh.wikipedia.org/wiki/%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88_(%E5%9B%BE%E8%AE%BA)
- 線上演算法ST表https://blog.csdn.net/liangzhaoyang1/article/details/52549822
- Tarjan演算法https://www.cnblogs.com/JVxie/p/4854719.html
- Tarjan演算法實現https://blog.csdn.net/freeelinux/article/details/54974044
相關文章
- lca
- 雙指標相關演算法指標演算法
- 【Lca 線上st演算法】hdu 2586 How far away ?演算法
- 求 LCA
- 加密型別以及相關演算法加密型別演算法
- 演算法學習筆記(5): 最近公共祖先(LCA)演算法筆記
- 【Lca 離線Tarjan演算法】hdu 2586 How far away ?演算法
- LCA最近公共祖先 線上演算法和離線演算法 模板演算法
- RMQ求lcaMQ
- 優化學習率相關演算法優化演算法
- 【演算法】HashMap相關要點記錄演算法HashMap
- 除法與GCD演算法的相關分析GC演算法
- 樹上問題/簡單演算法 LCA【最近公共祖先】演算法
- 最近學習了限流相關的演算法演算法
- 字串相關演算法1-字串旋轉字串演算法
- POJ 1330 LCA最近公共祖先 離線tarjan演算法演算法
- DFN 序求 LCA
- LCA + 樹上倍增
- LCA(倍增與Tarjan)
- TypeScript實現陣列相關簡單演算法TypeScript陣列演算法
- python 計算矩陣的相關演算法Python矩陣演算法
- 演算法問題總結-連結串列相關演算法
- LCA學習筆記筆記
- 樹上公共祖先(LCA)
- SolrCloud相關概念和兩種路由演算法介紹SolrCloud路由演算法
- 淺談倍增法求解LCA
- 【資源】NLP 演算法工程師相關的面試題演算法工程師面試題
- 用電負荷相關聚類演算法總結(1)聚類演算法
- 記錄資料相關的演算法整理(持續更新)演算法
- 買賣股票相關演算法-動態規劃-python演算法動態規劃Python
- PHP相關PHP
- MyBatis相關MyBatis
- Docker相關Docker
- swift相關Swift
- Oracle相關Oracle
- Spark相關Spark
- oracle 相關Oracle
- 相關工具