LCA Tarjin 并查集 离线

以前了解的Tarjin算法是用来求连通分量,在这里是用来求最近公共祖先

并查集用过,很有意思的工具。Kraskal算法是并查集最经典的应用。首先初始化集合,每个元素为单个集合。集合,我们需要选出一个代表,这个代表的性质是set[u] = u。一开始一个集合只有一个元素,所以该集合的代表也就是该元素。当两个集合需要合并时,例如:set[a] = a, set[b] = b。要是把a当作合并后集合的代表,那就让set[b] = a。这样,在find函数中,当发现set[u] != u时,意思就是,u这个元素,是和set[u]这个元素属于同一集合,然后就递归查找,直到set[v] = v,然后回退,更新set[u]。这个步骤叫做“路径压缩”。例如,set[a] = b; set[b]=c; set[c]=d;...., set[y] = z, set[z]=z,当开始find(a)后,会逐步深入到set[z]=z停止,然后回退,令set[y] = z, set[x] = z, set[w] = z, ... set[a] = z,这样,下次再find(a)时,就能很快找到set[z]=z结束,路径是不是变短了呢。

“离线”的意思是,需要预先知道所有的提问。那“在线”的意思就是不需要提前知道问题,你随便问,即刻回答。


先看一下这个参考资料:http://blog.csdn.net/ljsspace/article/details/66910300

这里去掉了参考资料中的ancestor数组。直接用子树的根节点来做集合的代表。从一个节点,走到另一个节点,必然要经过而且最先经过这两个节点的最近公共祖先。所以当发现一个节点A访问了时,马上查询另一个另一个节点B,如果该节点已访问,那B所在集合的代表,就是A和B的最近公共祖先。


#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#define M 50
#define C 50
#define Q 50

typedef struct _Node{
	int data;
	int chs;
	struct _Node* ch[C];
}Node, *pNode;

typedef struct _Query{
	int s, e;
	int anc;
}Query, *pQuery;
Query que[Q];

int n, q;
int set[M];
int mk[M];

pNode root;

void init(){
	int i;
	for(i=0; i<n; i++)
		set[i] = i;
}

int find(int u){
	if(u!=set[u])
		set[u] = find(set[u]);
	return set[u];
}

void combine(int u, int v){
	set[v] = u;
}

void create(pNode* r){
	int i;
	int v, c;
	scanf("%d %d", &v, &c);
	*r = (pNode)malloc(sizeof(Node));
	(*r)->data = v;
	(*r)->chs = c;
	for(i=0; i<c; i++){
		create(&((*r)->ch[i]));
	}
}

void show(pNode r){
	int i;
	printf("%d ", r->data);
	for(i=0; i<r->chs; i++)
		show(r->ch[i]);
}

void LCA(pNode r){
	int i;
	int t;
	for(i=0; i<r->chs; i++){
		LCA(r->ch[i]);
		combine(r->data, r->ch[i]->data);
	}
	mk[r->data] = 1;
	for(i=0; i<q; i++){
		t = -1;
		if(r->data==que[i].s && mk[que[i].e])
			t = que[i].e;
		else if(r->data==que[i].e && mk[que[i].s])
			t = que[i].s;
		if(t!=-1)
			que[i].anc = find(t);
	}
}

void main(){
	int i;
	freopen("in.txt", "r", stdin);
	while(scanf("%d %d", &n, &q)!=EOF){
		init();
		root = 0;
		create(&root);

		for(i=0; i<q; i++){
			scanf("%d %d", &(que[i].s), &(que[i].e));
		}
		memset(mk, 0, sizeof(mk));
		LCA(root);
		
		for(i=0; i<q; i++)
			printf("LCA of %d and %d is: %d\n", que[i].s, que[i].e, que[i].anc);
	}
}

数据:

20 19
0 2
1 3
3 0
4 2
8 2
12 0
13 0
9 2
14 0
15 0
5 2
10 2
16 0
17 0
11 2
18 0
19 0
2 2
6 0
7 0
8 5
12 14
14 6
6 7
10 9
3 10
14 15
15 8
11 10
13 12
5 4
18 13
4 17
16 19
9 19
10 19
7 17
17 16
18 19

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值