单源最短路径Bellman-Ford算法

对一个带权有向图G=(V,E),给定一个源顶点S,找出S到图中其他顶点v的最短路径即单源最短路径问题。该问题还有很多变体,像单终点最短路径、单对顶点最短路径、每对顶点间的最短路径等等。

最短路径问题是具有最优子结构的:一对顶点间的最短路径包含了该路径上的顶点间的最短路径。直观上理解,如果该路径上的两个顶点间的路径pij不是最短路径,那么用这两个顶点间的最短路径代替pij,那么就会出现一条更短的路径,与前面所说的最短路径矛盾。(具体证明参见算法导论P358)。

需要说明的是负权值边和松弛技术。Dijkstra算法是不允许图中存在负权边的,否则无法得到正确的结果。而Bellman-ford算法就允许图中存在负权边,而且该算法可以检测图中是否存在负权回路。两种算法都用到了松弛技术。即对边(uv),如果通过u到达v比当前找到的到v的最短路径还短,那么就更新d[v]parent[v]。通过松弛,可以减小最短路径估计。

Bellman-ford算法:

因为图中任意两个顶点的最短路径最多包含|V|-1条边,所以至多对每条边进行|V|-1次松弛后就会得到任意两个顶点间的实际最短路径。如果还能通过松弛降低最短路径估计,那么就可以断定图中存在负权回路,因为如果从sv的路径中包含负权回路,那么sv的最短路径长度就是负无穷了。可以这样理解,第ii>=1)次松弛得到的是源点s到每个顶点vV的路径长度为i的最短路径,第|V|-1次松弛得到的就是长度为|V|-1的最短路径。不过,显然不是每个顶点到s的最短路径长度都是|V|-1,所以对每条边都进行|V|-1次松弛操作是没有必要的。Bellman-ford的时间复杂度为O(VE)。可以对该算法进行简单的优化,如果本次循环并未对任何一条边进行松弛,那么可以判定已经得到了最终结果,退出循环。

如图所示:

《单源最短路径Bellman-Ford算法》

《单源最短路径Bellman-Ford算法》

代码如下:

#include<iostream>
#include<list>
using namespace std;

#define MAXVALUE 10000			//定义一个最长路径 

//此处Prim算法的图为有向图

struct Edge
{
	int verno;			//邻接数组中节点编号
	int weight;			//权值
	Edge* next;			//指向下一条边
};

struct Vertex
{
	Edge *adj;			//所指向的节点所在边
	int verno;			//邻接数组中节点编号
	char key;			//关键字
};

struct Graph
{
	Vertex *vertexs;	//节点数组
	int vertexnum;		//节点个数
	int adjnum;			//边数
};

class MSWBellmanFord
{
public:
	MSWBellmanFord(char *vertex,int vernum,char adj[][2],int *weight,int adjnum);
	void BellmanInsert(int source,int dest,int weight);
	int BellmanFindKey(char key);
	void BellmanInitSingleSource();
	bool BellmanMSW(char sourceKey);
	void BellmanOutput();
private:
	int *swayweight;
	int *parent;
	Graph *bfordGraph;
};

MSWBellmanFord::MSWBellmanFord(char *vertex,int vernum,char adj[][2],int *weight,int adjnum)
{
	int i,source,dest;

	swayweight = new int[vernum];
	parent = new int[vernum];
	bfordGraph = new Graph;

	bfordGraph->vertexs = new Vertex[vernum];
	bfordGraph->adjnum = adjnum;
	bfordGraph->vertexnum = vernum;
	for(i = 0;i < vernum;i++)
	{
		bfordGraph->vertexs[i].key = vertex[i];
		bfordGraph->vertexs[i].verno = i;
		bfordGraph->vertexs[i].adj = NULL;
	}

	for(i = 0;i < adjnum;i++)
	{
		source = BellmanFindKey(adj[i][0]);
		dest = BellmanFindKey(adj[i][1]);
		BellmanInsert(source,dest,weight[i]);
		//BellmanInsert(dest,source,weight[i]);			//无向图与有向图的区别在此
	}
}

void MSWBellmanFord::BellmanInsert(int source,int dest,int weight)
{
	if(bfordGraph->vertexs[source].adj == NULL || bfordGraph->vertexs[source].adj->weight > weight)
	{
		Edge* newnode = new Edge;
		newnode->verno = dest;
		newnode->weight = weight;
		newnode->next = bfordGraph->vertexs[source].adj;
		bfordGraph->vertexs[source].adj = newnode;
	}
	else
	{
		Edge* temp = bfordGraph->vertexs[source].adj;
		while(temp->next != NULL)						//插入新边的时候,把权值从低到高进行排序
		{
			if(temp->next->weight > weight)
				break;
			temp = temp->next;
		}
		Edge* newnode = new Edge;
		newnode->verno = dest;
		newnode->weight = weight;
		newnode->next = temp->next;
		temp->next = newnode;
	}
}

int MSWBellmanFord::BellmanFindKey(char key)
{
	int i;
	for(i = 0;i < bfordGraph->vertexnum;i++)
	{
		if(bfordGraph->vertexs[i].key == key)
			break;
	}
	return i;
}

void MSWBellmanFord::BellmanInitSingleSource()
{
	int vernum = bfordGraph->vertexnum;
	for(int i = 0;i < vernum;i++)
	{
		swayweight[i] = MAXVALUE;
		parent[i] = i;
	}
}

bool MSWBellmanFord::BellmanMSW(char sourceKey)
{
	int location = BellmanFindKey(sourceKey);
	int vernum = bfordGraph->vertexnum;
	int i,j;
	Edge *temp;
	BellmanInitSingleSource();
	//swayweight[0] = 0;									//这里为了偷懒,没有随意指定source,location本来是代表source的下标的
	swayweight[location] = 0;
	for(i = 0;i < vernum; i++)
	{
		/*
		for(j = 0;j < vernum; j++)
		{
			temp = bfordGraph->vertexs[j].adj;
			while(temp != NULL)
			{
				if((temp->weight + swayweight[j]) < swayweight[temp->verno])
				{
					swayweight[temp->verno] = temp->weight + swayweight[j];
					parent[temp->verno] = j;
				}
				temp = temp->next;
			}
		}
		*/
		temp = bfordGraph->vertexs[location].adj;
		while(temp != NULL)
		{
			if((temp->weight + swayweight[location]) < swayweight[temp->verno])
			{
				swayweight[temp->verno] = temp->weight + swayweight[location];
				parent[temp->verno] = location;
			}
			temp = temp->next;
		}
		j = (location + 1) % vernum;
		while(j != location)
		{
			temp = bfordGraph->vertexs[j].adj;
			while(temp != NULL)
			{
				if((temp->weight + swayweight[j]) < swayweight[temp->verno])
				{
					swayweight[temp->verno] = temp->weight + swayweight[j];
					parent[temp->verno] = j;
				}
				temp = temp->next;
			}
			j = (j + 1) % vernum;
		}
		
	}

	for(j = 0;j < vernum; j++)
	{
		temp = bfordGraph->vertexs[j].adj;
		while(temp != NULL)
		{
			if((temp->weight + swayweight[j]) < swayweight[temp->verno])
			{
				return false;
			}
			temp = temp->next;
		}
	}
	return true;
}

void MSWBellmanFord::BellmanOutput()
{
	int i,j,weight;
	list<int> route;
	cout<<"All the most shortest route from source : "<<endl;
	for(i = 0;i < bfordGraph->vertexnum;i++)
	{
		j = i;
		weight = swayweight[j];
		do
		{
			route.push_front(j);
			j = parent[j];

		}while(parent[j] != j);

		cout<<bfordGraph->vertexs[j].key;
		cout<<"---"<<bfordGraph->vertexs[route.front()].key;
		route.pop_front();
		while(!route.empty())
		{
			if(route.front() != j)
				cout<<"---"<<bfordGraph->vertexs[route.front()].key;
			route.pop_front();
		}
		cout<<"    "<<weight<<endl;
	}

}

int main()
{
	char vertex[] = {'S','T','X','Y','Z'};
	int vernum = 5;
	char adj[][2] = {{'S','T'},{'S','Y'},{'T','X'},{'T','Y'},{'T','Z'},{'X','T'},{'Y','X'},{'Y','Z'},{'Z','S'},{'Z','X'}};
	int weight[] = {6,7,5,8,-4,-2,-3,9,2,7};
	int adjnum = 10;
	MSWBellmanFord *bellford = new MSWBellmanFord(vertex,vernum,adj,weight,adjnum);
	bellford->BellmanMSW('S');
	bellford->BellmanOutput();

	return 0;
}

结果如下:

All the most shortest route from source :
S---S    0
S---Y---X---T    2
S---Y---X    4
S---Y    7
S---Y---X---T---Z    -2
请按任意键继续. . .

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/yujiaming493323/article/details/8771571
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞