package LCA;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.Scanner;
/**
* @Author:
* @Date : 2020/8/20 8:44
* Tarjan算法基于深度优先搜索的框架,对于新搜索到的一个结点,首先创建由这个结点构成的集合,再对当前结点的每一个子树进行搜索,
* 每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询问的结果必然在这个子树之外,
* 这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先。之后继续搜索下一棵子树,
* 直到当前结点的所有子树搜索完。这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问,
* 如果有一个从当前结点到结点v的询问,且v已被检查过,则由于进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,
* 而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v所在集合的祖先。
**/
public class POJ1330_20200820 {
// 并查集部分
// 父节点
static int[] p;
//并查集是一种数据结构:某个元素是否属于某个集合,或者 某个元素 和 另一个元素是否同属于一个集合
static int find(int u){
if(p[u]!=u){
p[u] = find(p[u]);
}
return p[u];
}
static class Edge{
int to;//这条边到达的点.
int next;//表示与第i条边同起点的的下一条边的存储位置.
}
// 链式前向星
static int[] head; //数组head[],它是用来表示以i为起点的第一条边存储的位置,实际上你会发现这里的第一条边存储的位置其实。
// 在以i为起点的所有边的最后输入的那个编号.
static Edge[] edges;
static boolean[] vist;
// 所求LCA的两点
static int qfrom;
static int qto;
// 找根
static boolean[] isroot;
static boolean LCA(int u){
// 在以自己为根的子树中计算LCA
// 如果没找到就回溯到父节点,再向下去兄弟子树计算
// 先建立以自己为代表的并查集
p[u]=u;//作为当前的根节点,将其父亲指向自己
vist[u]=true;//标记该点已经走过
for(int k=head[u];k>=0;k=edges[k].next){//查找子节点
int to = edges[k].to;
if(!vist[to]) {
// 计算子树
// 如果在某棵子树计算过程中算出结果,就返回true
if(LCA(to))
return true;
// 子树计算完,把子树的并查集代表节点设为当前节点
p[to] = u;
}
}
// 所有子树计算完毕,还没发现结果
// 试试用当前节点计算结果
if(u == qfrom){
if(vist[qto]){
System.out.println(find(qto));
return true;
}
}else if(u==qto){
if(vist[qfrom]){
System.out.println(find(qfrom));
return true;
}
}
// 还是没算出结果,向上回溯,准备去兄弟子树计算
return false;
}
public static void main(String[] args) throws FileNotFoundException {
//System.setIn(new FileInputStream(new File("POJ_1330.txt")));
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while (n-- > 0){
int m = sc.nextInt();
p = new int[m+1];
head = new int[m+1];
Arrays.fill(head,-1);
edges = new Edge[m];
vist = new boolean[m+1];
isroot = new boolean[m+1];
Arrays.fill(isroot,true);
// 链式前向星构有向图
for(int i=0;i<m-1;i++){
int from = sc.nextInt();
int to = sc.nextInt();
Edge edge = new Edge();//加入一条from 到 to 的单向边
edge.to=to;
edge.next=head[from];
edges[i]=edge;
head[from]=i;
// 有父节点的不是根
isroot[to]=false;
}
qfrom = sc.nextInt();
qto = sc.nextInt();
// 找根
int root=0;
for(int i=1;i<isroot.length;i++){
if(isroot[i]) {
root = i;
break;
}
}
// 计算LCA
LCA(root);
}
}
}
lca_trajan
最新推荐文章于 2021-02-01 15:32:34 发布