• 单源最短路径—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;
    }
    
    


     


     

  • 相关阅读:
    struts总结
    struts的MVC详细实现
    struts的由来
    Hibernate的搭建及使用
    Hibernate简介
    泛型
    eclipse手动添加源码
    beanUtils操作bean的属性
    ref:JAVA之Forward和Redirect的区别
    ref:下一个项目为什么要用 SLF4J
  • 原文地址:https://www.cnblogs.com/riskyer/p/3341592.html
Copyright © 2020-2023  润新知