用Dijkstra贪心算法求解单源最短路径问题

实验名称
用Dijkstra贪心算法求解单源最短路径问题。

实验目的
用Dijkstra贪心算法求解单源最短路径问题。

实验原理
使用基于贪心算法的dijkstra算法,能准确的输出用例中的单源最短路径, 并计算出程序运行所需要的时间。

实验步骤
① 将用例数据从文件中读取到数组中,初始化所有数组包括dis数组和visit数组;
②选取dis数组中尚未访问的最小值结点u,标记该结点并且以该节点为出发点,遍历该节点能够到达的所有未被访问的顶点v;
③若遍历到的顶点v此时的dis最短路径值大于起始结点经过u结点到达v结点的路径长度,则说明经u结点到达v结点的路径长度更优,进行松弛操作,并且将prev设置为u;
④重复②③步骤直到我们所要求的最终结点设置为已访问;
⑤输出结果。

时间复杂度分析
对于一个具有n个顶点和e条边的带权有向图,如果用邻接表表示,则需要O(ne)的时间复杂度。

实验心得
通过这次实验,我回顾了贪心算法的基本原理和实现dijkstra算法的代码。
dijkstra.cpp

#include <iostream>
#include <fstream>
#include <time.h> 
#include <windows.h>
using namespace std;
#define MAX 99999999
int dijkstra();
int e[1001][1001];
int vis[1001];//访问标记 
int dis[1001];//源结点到各个结点的最短距离 
int pre[1001]; //路径上当前节点的前一个结点(构造最优解) 
int n, m,be,ov;
double t;
ifstream ifile("input.txt");
ofstream ofile("output.txt");
void init()
{
	 ifile >> n >> m;
    // 初始化邻接矩阵 
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            e[i][j]=MAX;  //表示无边 
        }
    }
    // 填充数据
    for (int i = 1; i <= m; i++)
    {
        int a, b, c;
        ifile >> a >> b >> c;
        e[a][b] = c;
        vis[i]=0;  //未访问 
        pre[i]=0;
        dis[i]=MAX;
    }
    ifile>>be>>ov;
	dis[be]=0;//源点赋0,从源点开始扩展 
}
void output(){
	ofile<<dis[ov]<<endl;
	int u=ov;
	int lj[n],x=0;
	while(u!=be){
		lj[x]=u;
		x++;
		u=pre[u];
	}
	lj[x]=be;
	for(int i=0;i<=x;i++){
		ofile<<lj[i]<<" ";
	}
	ofile<<endl<<"Time is "<<t*1000<<"ms\n"; 
}
int main()
{
	LARGE_INTEGER frequency;
 	double v,beginoftime,endoftime,dt;//时钟的频率,起始,实际时间差,实际时间 
 	//样例 
 	init();
 	QueryPerformanceFrequency(&frequency);//获得时钟频率
 	v=(double)frequency.QuadPart;//取得频率*/
 	QueryPerformanceCounter(&frequency);
    beginoftime=frequency.QuadPart;//获得初始值
	
	cout<<dijkstra();
	
 	QueryPerformanceCounter(&frequency);
    endoftime=frequency.QuadPart;//获得终止值
    dt=(double)(endoftime-beginoftime);//差值
    t=dt/v;//差值除以频率得到时间
	cout<<endl<<"Time is "<<t*1000<<"ms\n"; 
	output();
	
	//10 
	ifile.close();
	ifile.open("10.txt"); 
	cout<<"10 SISE:\n";
	init();
 	QueryPerformanceFrequency(&frequency);//获得时钟频率
 	v=(double)frequency.QuadPart;//取得频率*/
 	QueryPerformanceCounter(&frequency);
    beginoftime=frequency.QuadPart;//获得初始值
	cout<<dijkstra();
 	QueryPerformanceCounter(&frequency);
    endoftime=frequency.QuadPart;//获得终止值
    dt=(double)(endoftime-beginoftime);//差值
    t=dt/v;//差值除以频率得到时间
	cout<<endl<<"Time is "<<t*1000<<"ms\n"; 
	
	//100
	ifile.close();
	ifile.open("100.txt"); 
	cout<<"100 SISE:\n";
	init();
 	QueryPerformanceFrequency(&frequency);//获得时钟频率
 	v=(double)frequency.QuadPart;//取得频率*/
 	QueryPerformanceCounter(&frequency);
    beginoftime=frequency.QuadPart;//获得初始值
	cout<<dijkstra();
 	QueryPerformanceCounter(&frequency);
    endoftime=frequency.QuadPart;//获得终止值
    dt=(double)(endoftime-beginoftime);//差值
    t=dt/v;//差值除以频率得到时间
	cout<<endl<<"Time is "<<t*1000<<"ms\n"; 
	
	//1000	
	ifile.close();
	ifile.open("1000.txt"); 
	cout<<"1000 SISE:\n";
	init();
 	QueryPerformanceFrequency(&frequency);//获得时钟频率
 	v=(double)frequency.QuadPart;//取得频率*/
 	QueryPerformanceCounter(&frequency);
    beginoftime=frequency.QuadPart;//获得初始值
	cout<<dijkstra();
 	QueryPerformanceCounter(&frequency);
    endoftime=frequency.QuadPart;//获得终止值
    dt=(double)(endoftime-beginoftime);//差值
    t=dt/v;//差值除以频率得到时间
	cout<<endl<<"Time is "<<t*1000<<"ms\n"; 
    return 0;
}
int dijkstra()
{
	int u=be;  //当前结点 ——源结点开始 
	int min = MAX;
    for (int i = 1; i <= n; i++)
    {
        min = MAX;
        // 寻找权值最小的点u
        for (int j = 1; j <= n; j++)
        {
            if (vis[j] == 0 && dis[j] < min)
            {
                min = dis[j];
                u = j;
            }
        }
        vis[u] = 1;
        if(u==ov) return dis[ov];  //目的结点——结束 
        for (int v = 1; v <= n; v++)
        {
            // 对于每个u可达的v来说
            if (e[u][v] < MAX&&vis[v]==0)
            {
                // 如果当前的dis[v]不满足三角形不等式,那么进行松弛操作
                if (dis[v] > dis[u] + e[u][v])     //经过当前结点u的路径要更小 
                {
                    dis[v] = dis[u] + e[u][v];
                    pre[v]=u;
                }
            }
        }
    }
}

graph.h

#ifndef GRAPH
#define GRAPH
template <typename VertexType>
class Graph {
private:
  void operator =(const Graph&) {}     // Protect assignment
  Graph(const Graph&) {}         // Protect copy constructor
public:
  Graph() {}          // Default constructor
  virtual ~Graph() {} // Base destructor
  // Initialize a graph of n vertices
  virtual void Init(int n) =0;
  // Return: the number of vertices and edges
  virtual int n() =0;
  virtual int e() =0;
  // Return v's first neighbor
  virtual int first(int v) =0;
 // Return v's next neighbor
  virtual int next(int v, int w) =0; 
	//找到(包含实际信息的)顶点在图中的位置 
  virtual int locateVex(VertexType u) =0;
  //返回某个顶点的值(实际信息) 
  virtual VertexType getVex(int v)=0; 
  //给某个顶点赋值
  virtual void putVex(int v,VertexType value) =0; 
  // Set the weight for an edge
  virtual void setEdge(int v1, int v2, int wght) =0;
  // Delete an edge
  // i, j: The vertices
  virtual void delEdge(int v1, int v2) =0;
  // Determine if an edge is in the graph
  // i, j: The vertices
  // Return: true if edge i,j has non-zero weight
  virtual bool isEdge(int i, int j) =0;
  // Return an edge's weight
  // i, j: The vertices
  // Return: The weight of edge i,j, or zero
  virtual int weight(int v1, int v2) =0;
  // Get and Set the mark value for a vertex
  // v: The vertex
  // val: The value to set
  virtual int getMark(int v) =0;
  virtual void setMark(int v, int val) =0; 
};
#endif

graphm.h

#include <iostream>
#include "graph.h"
#define MAX_VERTEX_NUM 40
#define UNVISITED 0
#define VISITED 1
using namespace std;
template <typename VertexType>
class Graphm : public Graph<VertexType> {
private:
  int numVertex, numEdge; //顶点数和边数
	VertexType vexs[MAX_VERTEX_NUM];	//存储顶点信息 
  int **matrix;           // Pointer to adjacency matrix
  int *mark;              // Pointer to mark array
public:
  Graphm(int numVert)     // Constructor
    { Init(numVert); }
  ~Graphm() {       // Destructor
    delete [] mark; // Return dynamically allocated memory
    for (int i=0; i<numVertex; i++)
      delete [] matrix[i];
    delete [] matrix;
  }
  void Init(int n) { // Initialize the graph
    int i;
    numVertex = n;
    numEdge = 0;
    mark = new int[n];     // Initialize mark array
    for (i=0; i<numVertex; i++)
      mark[i] = UNVISITED;
    matrix = (int**) new int*[numVertex]; // Make matrix
    for (i=0; i<numVertex; i++)
      matrix[i] = new int[numVertex];
    for (i=0; i< numVertex; i++) // Initialize to 0 weights
      for (int j=0; j<numVertex; j++)
        matrix[i][j] = -1;
  }
  int n() { return numVertex; } // Number of vertices
  int e() { return numEdge; }   // Number of edges
  // Return first neighbor of "v"
  int first(int v) {
    for (int i=0; i<numVertex; i++)
      if (matrix[v][i] != -1) return i;
    return numVertex;           // Return n if none
  }
  // Return v's next neighbor after w
  int next(int v, int w) {
    for(int i=w+1; i<numVertex; i++)
      if (matrix[v][i] != -1)
        return i;
    return numVertex;           // Return n if none
  }
	/**返回顶点在图中的位置**/ 
	int locateVex(VertexType u){
		for(int i=0;i<numVertex;i++){
			if(u==vexs[i])	
				return i;
		}
		return -1;
	}
	/**返回某个顶点的值(实际信息) **/ 
	VertexType getVex(int v){
		return vexs[v];
	} 
  /**给某个顶点赋值**/
	void putVex(int v,VertexType value){
		vexs[v]=value;
	}
		
  // Set edge (v1, v2) to "wt"
  void setEdge(int v1, int v2, int wt) {
    if (matrix[v1][v2] == -1) 
			numEdge++;
    matrix[v1][v2] = wt;  
  }
  void delEdge(int v1, int v2) { // Delete edge (v1, v2)
    if (matrix[v1][v2] != -1){
    	numEdge--;
    	matrix[v1][v2] = -1; 
	}
}
  bool isEdge(int i, int j) // Is (i, j) an edge?
  { return matrix[i][j] != -1; }
  int weight(int v1, int v2) { return matrix[v1][v2]; }
  int getMark(int v) { return mark[v]; }
  void setMark(int v, int val) { mark[v] = val; }
};

main.cpp

#include<iostream>
#include<cstring>
#include "graphm.h"
#include "graph.h"
Graph<int>*  createGraph();
void Gprint(Graph<int>* G);
int dijkstra(Graph<int>* G,int,int,int*,int*);
int main() {
	Graph<int>* G;
	G=createGraph();
	int dist[G->n()];
	int pre[G->n()]={0};
	cout<<dijkstra(G,1,5,dist,pre)<<endl;
	return 0;
}
Graph<int>*  createGraph()
{
  int i, v1, v2, dist;
	int n,m;
	cin>>n>>m;
	Graph<int>* G;
    G = new Graphm<int>(n);
	int ver;
	//存储顶点信息
	for(i=0;i<n;i++){
		cin>>ver;
		G->putVex(i,ver); 
	} 
	int tv1,tv2;
    for(int j=0;j<m;j++){
	cin>>tv1>>tv2>>dist;
  	//找到第一个顶点在图中的位置
  	v1 = G->locateVex(tv1);
		//找到第二个顶点在图中的位置
	v2 = G->locateVex(tv2);
	G->setEdge(v1,v2,dist); 
  }	
  return G;
}

int dijkstra(Graph<int>* G,int u,int s,int *dist,int *pre)
{
	int begin = G->locateVex(u);
	int end = G->locateVex(s);
	int i;
	for(i=0;i<G->n();i++){
	    dist[i]=G->weight(begin,i);
	    if(dist[i]==-1)dist[i]=1000000;
	}
	dist[begin]=0;pre[begin]=-1;
	int now=begin,nowmin;
	while(G->getMark(end)==0){  //一直处理到结束 
		nowmin=10000001;
		for(i=0;i<G->n();i++){
			if(G->getMark(i)==0&&dist[i]<nowmin){
				now=i;nowmin=dist[i]; 
			}
		} 
		G->setMark(now,1);
		for(i=G->first(now);i<G->n();i=G->next(now,i)){
			if(dist[i]>dist[now]+G->weight(now,i)&&G->getMark(i)==0)
				dist[i]=dist[now]+G->weight(now,i);
				pre[i]=now;
		}
	}
	//最优解 
	i=end;
	int j=1;
	int lj[G->n()];
	lj[0]=G->getVex(end);
	while(i!=begin){
		i=pre[i];
		lj[j]=G->getVex(i);
		j++;
	}
	for(i=j-1;i>=0;i--){
		cout<<lj[i]<<" ";
	}
	cout<<endl;
	return dist[end];
}

生成.cpp

#include <iostream>
#include <time.h>
#include <fstream>
//***本程序用于产生随机数据
using namespace std;//RAND_MAX=32767
int a[1000]={0};
int b[1000]={0};
int c[1000]={0};
int n,m;
int Size[3]={10,100,1000};//文件大小
void print(ofstream &outfile,int n,int m)//输出到文件 
{
	outfile<<n<<" "<<m<<endl;
	for(int i=0;i<n;i++)
	{
		outfile<<a[i]<<' '<<b[i]<<' '<<c[i]<<endl;
	}
	outfile<<1<<' '<<n;
}
int main()
{
    int n=0;
    //ifstream
    ofstream out_10("10.txt"),out_100("100.txt"),out_1000("1000.txt");//输入代查找数据;
    srand(time(NULL));//时间种子
    //生成测试文件
    for(int i=0;i<Size[0];i++) 
	{	
		m=rand()%20+1; //边数 
		a[i]=rand()%10+1;//得到[0,10)内的数
		b[i]=rand()%10+1;
		c[i]=rand()%100+1; 
	}
    print(out_10,Size[0],m);
    for(int i=0;i<Size[1];i++) 
	{
		m=rand()%100+100; //边数 
		a[i]=rand()%10+1;//得到[0,10)内的数
		b[i]=rand()%10+1;
		c[i]=rand()%100+1; 
	}
    print(out_100,Size[1],m);
    for(int i=0;i<Size[2];i++) 
	{
		m=rand()%500+500; //边数 
		a[i]=rand()%10+1;//得到[0,10)内的数
		b[i]=rand()%10+1;
		c[i]=rand()%100+1; 
	}
    print(out_1000,Size[2],m);
}
 
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值