lca(Tarjan)

本文深入讲解Tarjan算法,一种利用并查集优化的标记算法,用于寻找图中的强连通分量和计算最低公共祖先(LCA)。文章详细解析了算法的实现过程,包括如何通过递归遍历图的节点,更新节点的父节点,以及如何处理查询以找到LCA。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

void Tarjan(int x){
//	这里的话我又要说一下了,这个所谓的Tarjan算法,其实是用并查集优化了 打标记的算法;
	vis[x] = 1;//这个是在打上标记标记其正在被遍历,但尚未遍历完所有子树;
	for(int i = head[x]; i ; i = Next[i]){//先枚举所有出边,
		int y = ver[i];
		if(vis[y]) continue;//如果这个点已被遍历,或者正在遍历(注:此种情况仅针对双向边,因为会从边的终点开始遍历时会从边的终点再向向起点遍历一次)
//		就continue,也就是不再遍历一次这个点
		Tarjan(y);
		f[y] = x;//当你遍历完所有子树时,就会进行递归,即向上回溯之类的(其实并不是回溯啦),这时对于这条边的另一个端点的遍历已经完成了,也就是这个
		//端点一直到叶节点都已经遍历完成了,这时就将这另一个端点的父亲节点指向其起点;(对于这个端点直到叶节点的所有点都会执行这个操作,
		//即他们的父亲节点都将是子树的根节点);
	}
	for(int i = H[x];i;i = ans[i].Next){//如果这个点及其子树都以遍历完成,那么,对查询进行访问,寻找是否要查询这个点,如果需要查询,
	//那么判断,其查询的另一个点是否以遍历完成,如果以便利,那么他们的LCA就是这另一个点的子树根节点;
	//这里继续解释一下,首先,你要查询的两个点,肯定会属于同一个子树(这个子树不一定是最小的那个),因为他们拥有一个公共祖先,所以以这个公共祖先为
	//根节点,将会得出一个树,其次,在这棵子树中,如果一个要查询的对象尚未遍历,而另一个已经遍历的话,就意味着他们向上的LCA也一定正在被遍历,
	//通过上面的分析我们得出了:  这两个点属于同一个子树,并且这个子树的根节点就是Lca,而这时已有一个点已经被遍历完全,那么,这个点的父亲节点已经被更新成了
	//这棵子树的根节点(因为一个点已经遍历完成时,他的父亲节点将会指向其上一个点,也就是这条以这个点为终点的边的起点,而这时,这已经遍历完的一个将要查询的点
	// 就会更新它到LCA的父亲节点,这是我们先假设,这两个我们要查询的点处在不同的路径上(也就是去其中一个深度较大的点在更新其父亲节点时不会
	//经过另一个节点)这时,有一个节点已经便利完全,而又有另一个不在同一路径上的节点未遍历完全,这就是表明了,他们的LCA也处于尚未便利完全的状况,
	//这里不妨用反证法证明一下,如果这个LCA已经遍历完全,就意味着,这个点的所有子树都已遍历完全,根本不可能存在有未遍历完全的点,这与我们的初始状态矛盾了)
	// 至此,就将这个为什么这另一个点的父亲节点就是LCA解释清楚了;
		int y = ans[i].y,id = ans[i].id;
		if(vis[y] == 2) {
			linlin = get(y);
		}
	}
	vis[x] = 2;//标记这个点以遍历完成;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值