实验名称
用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);
}