非递归实现有向无环图最长路径查找

本文介绍了一种求解有向无环图中最长路径的高效算法,通过迭代而非递归的方式更新节点的far值来逐步逼近最长路径,最终确定各终点的最长路径。

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

由于递归的效率过低,当图非常大的时候有可能炸栈,所有采用循环迭代的方法。这里会比较严格地证明算法的正确性。
对于一个有向无环图,将至少存在一个入度为0的结点。显然,若不存在的话这将是一个有环图。

同时,最长路径的起点一定是入度为0的结点,我们就可以把所有入度为0的结点的far值设为0。入度不为0的far都设-1(方便起见)。

要注意,这个far其实类似于算导中的松弛操作,是经过当前结点的各个路径上起始点与该节点的距离的最大值的下界,听起来可能不好理解。举个例子,如果图中只有一个度为0的结点A,那么A到C的最长距离一定大于等于C结点的far值。
那么我们有了初始一堆度为0的结点之后,就可以用他们去更新各自的邻结点。要知道,far虽然是一个下界,但这个下界是可达到的。不妨设结点的MAX值为以该节点为终点的路径长度最大值,从动态规划的角度出发:

                    q.MAX=max((p.MAX+<p,q>)p为q的各个父节点)
其中<p,q>为pq边的权重。

接下来是一个循环不变式,若某个时刻度为0的结点的far值都等于各自的MAX值,则将当前所有入度为0的结点更新各自邻接点后再删除当前度入为0的结点以及相关的边(图中其余相关的结点的度也会改变),得到的新的图中度为0的结点的far值仍等于各自MAX值。
那么这里可能会有个小问题,如果我删除了当前入度为0的结点,那新图中一定会存在入度为0的结点吗??答案是一定的,由于当原来无环时删除个别结点后更不能有环,根据我们引的第一个定理可知道一定存在。这样每时每刻删除后都会有新的入度为0的结点,于是循环是可以一直进行的,遇到出度为0的结点说明是某一终点(显然终点可以有多个,但各自的MAX值不一样),于是把该点的far值都进一个容器最后排个序就可以了。
但这里我们可以不需要一批一批度为0的结点去更新删除,而是每次都操作一个入度为0的结点就可以。

也就是每次取结点中度最小的(由上知必为0),然后更新邻接点,删除它,减少邻接点的度。
不断重复,就可以保证最后一定能求出最长路径。
下面附上源代码。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef struct Node//结点
{
	int key;
	struct near_Node*next;
	int far;
	int degree;
}Node;
typedef struct near_Node//邻接点
{
	int key;
	int power;//权重
	struct near_Node*next;
}near_Node;
bool cmp(Node&A, Node&B)
{
	return A.degree< B.degree;
}
int main()
{
	vector<Node>U;
	Node node[1000];
	int i,a,b,c;
	near_Node* temp,*temp1;
	for (i = 0; i < 1000; i++)//初始化
	{
		node[i].key = -1;
		node[i].next = NULL;
		node[i].far =0;
		node[i].degree = 0;
	}
	for (;;)//输入图
	{
		cin >> a >> b;
		if (a == -1 && b == -1)break;
		cin >> c;
		temp = node[a].next;
		node[a].next = new near_Node;
		temp1 = node[a].next;
		temp1->key = b;
		temp1->power = c;
		temp1->next = temp;
		node[b].degree++;
		if (node[a].key == -1)node[a].key = a;
		if (node[b].key == -1)node[b].key = b;
	}
	for (i = 0; i < 1000; i++)
	{
		if (node[i].key != -1)
		{
			if (node[i].degree == 0)
			{
				node[i].far = 0;
			}
			U.push_back(node[i]);
		}
	}//将所有结点入U
	Node T;
	vector<int>final;
	for (;!U.empty();)
	{
		sort(U.begin(), U.end(), cmp);
		T = *U.begin();
		if (T.next == NULL)final.push_back(T.far);
		for (temp=T.next;temp!=NULL;temp=temp->next)//对于T的邻接表的每个元素
		{
			for (auto&x:U)
			{
				if (x.key == temp->key)
				{
					x.degree--;
					x.far = max(x.far, T.far + temp->power);
				}
			}
		}
		U.erase(U.begin());
	}
	sort(final.begin(), final.end());
	cout << *(final.end() - 1);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值