单源最短路径—Bellman-Ford和Dijkstra算法

Bellman-Ford算法:通过对边进行松弛操作来渐近地降低从源结点s到每个结点v的最短路径的估计值v.d,直到该估计值与实际的最短路径权重相同时为止。该算法主要是基于下面的定理:

         设G=(V,E)是一带权重的源结点为s的有向图,其权重函数为W,假设图G中不包含从源结点s可到达的权重为负值的环路,在对图中的每条边执行|V|-1次松弛之后,对于所有从源结点s可到达的结点v,都有

  证明:s可到达结点v并且图中没有权重为负值的环路,所以总能找到一条路径p=(v0,v1,...,vk)是从s到v结点的最短路径,这里v0=s,vk=v。因为最短路径都是简单路径,p最多包含|V|-1条边,即k<=|V|-1。由于v0=s,所以,当对所有的边进行第1次松弛后,必有,依次类推,进行第k次松弛后,必有,最后可得进行|V|-1次松弛后有

下面证明为什么当,对边松弛后,有:

由于s->...->vi-1->vi是一条最短路径,在对边松弛后,

                                                                                                        有:(这个是松弛的定义)。

                                                                                                                          

                                                                                                                                                 

又由于,所以:

Bellman-Ford算法的实现是对图中的每条边进行|V|-1次松弛。

Dijkstra算法:将图中的结点分为两类,一类是结点集合S,从源结点s到集合中每个结点之间的最短路径已经被找到。另一类集合是V-S。算法重复地从集合V-S中选择最短路径估计最小的结点u,然后将u加入到集合S,然后对所有从u出发的边进行松弛。在进行|V|次重复操作后,其中每条边经历过一次松弛,对于所有的结点v,都有。关键点是证明:该算法在每次选择结点u来加入到集合S时,有。证明过程省略,可以参考《算法导论》的证明过程。

下面给两种算法的出程序:在Dijkstra算法中,通过结点的颜色color来区分结点是属于S集合还是V-S集合,黑色时是S集合中,白色时是V-S集合中

Minpath.h

#pragma once
#include<iostream>
#include<string>
#include<vector>
using namespace std;

template<typename Comparable>
struct Edge;
template<typename Comparable>
struct Node
{
	Comparable element;//结点的元素
	vector<Edge<Comparable>*>Side;//该结点所在的边
	Node<Comparable>* T;    //最短路径中该结点的父亲
	int dis;                               //距离
	string color;            //在Dijkstra算法中用于标记该结点是否被选中
	Node(Comparable e,Node<Comparable>* f,int d,string c)
	{
		element=e;
		T=f;
		dis=d;
		color=c;
	}
};
template<typename Comparable>
struct Edge
{
	Node<Comparable>* N1;  //边的两端结点,N1是N2结点的父结点
	Node<Comparable>* N2;
	string color;           
	int weight;
	Edge(Node<Comparable>* n1,Node<Comparable>* n2,int w):N1(n1),N2(n2),weight(w){}
};

template<typename Comparable>
class graph
{
public:
	void insert(Comparable *a,int *matrix,int *w,int n);//a:图中个结点的元素;matrix:邻接矩阵
	void Bellman(Comparable x);
	void Dijkstra(Comparable x);
	void MinPath(Comparable x);
private:
	vector<Node<Comparable>*> root;
	vector<Edge<Comparable>*> side;
	Node<Comparable>* find(Comparable x);
	Node<Comparable>* find();
	void relax(Edge<Comparable>* edge);            //松弛
	void MinPath(Node<Comparable>* s);
};

 

Minpath.cpp

#include "stdafx.h"
#include"Minpath.h"
#include<iostream>
#include<string>
#include<vector>
using namespace std;

template<typename Comparable>
void graph<Comparable>::insert(Comparable *a,int *matrix,int *w,int n)
{
	for(int i=0;i<n;i++)
	{
		Node<Comparable>* node=new Node<Comparable>(a[i],NULL,10000,"WHITE");
		root.push_back(node);
	}
	Node<Comparable>* node=NULL;
	Node<Comparable>* temp=NULL;
	int k=0;
	for(int i=0;i<n;i++)
	{
		node=root[i];
		for(int j=0;j<n;j++)
		{
			if(matrix[n*i+j]!=0)
			{
				temp=root[j];
				Edge<Comparable>* edge=new Edge<Comparable>(node,temp,w[k]);
				k=k+1;
				side.push_back(edge);
				node->Side.push_back(edge);
			}
		}
	}
}
//找出元素是x的结点
template<typename Comparable>
Node<Comparable>* graph<Comparable>::find(Comparable x)
{
	int n=root.size();
	Node<Comparable>* temp=NULL;
	for(int i=0;i<n;i++)
	{
		if(root[i]->element==x)
			temp=root[i];
	}
	return temp;
}
//边的松弛
template<typename Comparable>
void graph<Comparable>::relax(Edge<Comparable>* edge)
{
	if(edge->N2->dis>edge->N1->dis+edge->weight)
	{
		edge->N2->dis=edge->N1->dis+edge->weight;
		edge->N2->T=edge->N1;
	}

}
//Bellman-Ford算法:对图中的边进行|v|-1次的松弛
template<typename Comparable>
void graph<Comparable>::Bellman(Comparable x)
{
	Node<Comparable>* s=find(x);
	bool flag=true;
	if(s==NULL)
		return;
	int n=root.size();
	int en=side.size();
	Edge<Comparable>* edge=NULL;
	s->dis=0;                  //选择s为源结点,并初始化其距离为0
	//对图中的每个边进行|V|-1次的松弛
	for(int i=0;i<n-1;i++)
	{
		for(int j=0;j<en;j++)
		{
			edge=side[j];
			relax(edge);            //松弛
		}
	}
	for(int i=0;i<en;i++)
	{
		edge=side[i];
		if(edge->N2->dis>edge->N1->dis+edge->weight)
			flag=false;
	}

	if(flag==false)
		cout<<"图中包含权重为负值的环路"<<endl;
	else
	{
		s->T=NULL;
	}

}
//Dijkstra算法
template<typename Comparable>
void graph<Comparable>::Dijkstra(Comparable x)
{
	Node<Comparable>* s=find(x);
	Node<Comparable>* source=s;
	if(s==NULL)
		return;
	Edge<Comparable>* edge=NULL;
	Node<Comparable>* temp=new Node<Comparable>(s->element,NULL,10000,"WHITE");
	Node<Comparable>* t=temp;
	int n=root.size();

	s->dis=0;
	s->color="BLACK";   //初始化源结点

	for(int i=0;i<n;i++)
	{
		int en=s->Side.size();
		for(int j=0;j<en;j++) //对s结点的所有的边进行一次松弛
		{
			edge=s->Side[j];
			relax(edge);
		}
	   s=find();
	   s->color="BLACK";
	}
	source->T=NULL;
}
template<typename Comparable>
Node<Comparable>* graph<Comparable>::find()
{
	Node<Comparable>* s=new Node<Comparable>(root[0]->element,NULL,10000,"WHITE");
	int n=root.size();
	for(int i=0;i<n;i++)
	{
		if((root[i]->color=="WHITE")&&(root[i]->dis<s->dis))
			s=root[i];
	}
	return s;
}
//找出某结点的最短路径并输出
template<typename Comparable>
void graph<Comparable>::MinPath(Comparable x)
{
	Node<Comparable>* s=find(x);
	cout<<"最短路径为:"<<endl;
	MinPath(s->T);
	cout<<"("<<s->element<<","<<s->dis<<")"<<endl;
}
template<typename Comparable>
void graph<Comparable>::MinPath(Node<Comparable>* s)
{
	if(s!=NULL)
	{
	MinPath(s->T);
	cout<<"("<<s->element<<","<<s->dis<<")"<<"—>";
	}
	else
		return;
}


Algorithm-graph3.cpp

// Algorithm-graph3.cpp : 定义控制台应用程序的入口点。
//主要是图中的最短路径问题:Bellman-Ford算法和Dijkstra算法

#include "stdafx.h"
#include"Minpath.h"
#include"Minpath.cpp"
#include<iostream>
#include<string>
#include<vector>
using namespace std;
#include<iostream>

int _tmain(int argc, _TCHAR* argv[])
{
	graph<string> g;
	////Bellman-Ford算法
/*	int n=5;
	int matrix[25]={0,1,0,0,1,
		            0,0,1,1,1,
					0,1,0,0,0,
					1,0,1,0,0,
					0,0,1,1,0};
	string a[5]={"s","t","x","z","y"};
	int w[10]={6,7,5,-4,8,-2,2,7,-3,9};*/ 

	//Dijkstra算法
	int n=5;
	int matrix[25]={0,1,0,0,1,
		            0,0,1,0,1,
					0,0,0,1,0,
					1,0,1,0,0,
					0,1,1,1,0};
	string a[5]={"s","t","x","z","y"};
	int w[10]={10,5,1,2,4,7,6,3,9,2};
	g.insert(a,matrix,w,n);
//	g.Bellman("s");
	g.Dijkstra("s");   //选择结点元素为s的作为源结点
	g.MinPath("x");    //输出结点元素是x的最短路径
	return 0;
}


 


 

 

posted on 2013-09-26 21:39  you Richer  阅读(325)  评论(0编辑  收藏  举报