一、最短总距离算法:
1.描述
我们先来分析一下这个问题。某个地区n个城市构成一个交通图,我们可以使用图结构来描述这个问题,其对应关系如下:
- 每个城市代表一个图中的一个顶点。
- 两个顶点之间的边就是两个城市之间的路径,边的权值代表了城市间的距离。
这样,求解各个城市之间的最短总距离问题就归结为该图的最小生成树问题。
2.最小生成树
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。如图:
3.基本思想
假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。算法从U={u0}(u0∈V)、TE={}开始。重复执行下列操作:
在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。
此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。
4.实现代码(最短总距离算法)
#include <iostream>
#include <time.h>
#include <string.h>
#define MaxNum 20 //图的最大顶点数
#define MaxValue 65535 //最大值
#define USED 0; //已选用顶点
#define NoL -1 //非邻接顶点
typedef struct
{
char Vertex[MaxNum]; //保存顶点信息(序号或字母)
int GType; //图的类型(0:无向图,1)
int VertexNum; //顶点的数量
int EdgeNum; //边的数量
int EdgeWeight[MaxNum][MaxNum]; //保存边的权
int isTrav[MaxNum]; //定义邻接矩阵图结构
}GraphMatrix;
void CreateGraph(GraphMatrix *GM)
{
int i,j,k;
int weight;
char EstartV,EendV;
std::cout<<"输入图中各顶点信息\n";
for (i = 0; i<GM->VertexNum; i++)
{
getchar();
printf("第%d个顶点:",i+1);
scanf("%c",&(GM->Vertex[i]));
}
std::cout<<"输入构成各边的顶点及权值:\n";
for (k = 0; k <GM->EdgeNum; k++)
{
getchar();
printf("第%d条边:",k+1);
scanf("%c %c %d",&EstartV,&EendV,&weight);
for ( i= 0; EstartV != GM->Vertex[i]; i++);
for ( j= 0; EendV != GM->Vertex[j]; j++);
GM->EdgeWeight[i][j] = weight;
if (GM->GType == 0) //若是无向图
{
GM->EdgeWeight[j][i] = weight;
}
}
}
void ClearGraph(GraphMatrix *GM)
{
int i,j;
for (i=0; i<GM->VertexNum; i++)
{
for (j=0; j<GM->VertexNum; j++)
{
GM->EdgeWeight[i][j] = MaxValue;
}
}
}
void OutGraph(GraphMatrix *GM)
{
int i,j;
for (j=0; j<GM->VertexNum; j++)
{
printf("\t%c",GM->Vertex[j]);
}
std::cout<<"\n";
for (i=0; i<GM->VertexNum; i++)
{
printf("%c",GM->Vertex[i]);
for (j=0; j<GM->VertexNum; j++)
{
if (GM->EdgeWeight[i][j] == MaxValue)
{
printf("\tZ"); //以Z表示无穷大
}
else
{
printf("\t%d",GM->EdgeWeight[i][j]); //输出边的权值
}
}
std::cout<<"\n";
}
}
void PrimGraph(GraphMatrix GM) //最小生成树算法
{
int i,j,k,min,sum;
int weight[MaxNum]; //权值
char vtempx[MaxNum]; //临时顶点信息
sum = 0;
for(i = 1; i<GM.VertexNum;i++) //保存邻接矩阵中的一行数据
{
weight[i] = GM.EdgeWeight[0][i];
if (weight[i] == MaxValue)
{
vtempx[i] = NoL;
}
else
{
vtempx[i] = GM.Vertex[0]; //邻接顶点
}
}
vtempx[0] = USED; //选用
weight[0] = MaxValue;
for (i = 1; i<GM.VertexNum; i++)
{
min = weight[0]; //最小权值
k = i;
for (j = 1; j<GM.VertexNum; j++)
{
if (weight[j] < min && vtempx[j]>0) //到具有更小权值的未使用边
{
min = weight[j]; //保存权植
k = j; //保存邻接点序号
}
}
sum +=min; //权值累加
printf("(%c,%c),",vtempx[k],GM.Vertex[k]); //输出生成树一条边
vtempx[k] = USED;
weight[k] = MaxValue;
for (j = 0; j<GM.VertexNum; j++) //重新选择最小边
{
if (GM.EdgeWeight[k][j] < weight[j] && vtempx[j]!= 0)
{
weight[j] = GM.EdgeWeight[k][j]; //权植
vtempx[j] = GM.Vertex[k];
}
}
}
printf("\n最小生成树的总权值为:%d\n",sum);
}
int main(int argc, const char * argv[])
{
// std::cout << "Hello, World!\n";
GraphMatrix GM;
char again;
printf("寻找最小生成树!\n");
printf("请先输入生成图的类型:");
scanf("%d",&GM.GType);
printf("输入图的顶点数量:");
scanf("%d",&GM.VertexNum);
printf("输入图的边数量:");
scanf("%d",&GM.EdgeNum);
ClearGraph(&GM);
CreateGraph(&GM);
printf("该图的邻接矩阵数据如下:\n");
OutGraph(&GM);
printf("最小生成树的边为:");
PrimGraph(GM);
printf("\n继续玩(y/n)");
fflush(stdin);
scanf("%c",&again);
printf("\n");
return 0;
}
演示结果:
演示效果图:
二、最短路径
1.描述
求解城市之间的最短距离是一个非常实际的问题。其大意如下:
某个地区有n个城市,如何选择路线使某个城市到某个指定城市的距离最短?
注:这里需要求解的最短路径指两个城市之间的最短距离,面不是所有城市之间最短总距离。
2.基本思想
算法步骤如下:
1. 初始时令 S={V0},T={其余顶点},T中顶点对应的距离值
若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值
若不存在<V0,Vi>,d(V0,Vi)为∞
2. 从T中选取一个其距离值为最小的顶点W且不在S中,加入S
3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值
重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止
3.实现代码
#include <iostream>
#include <time.h>
#include <string.h>
#define MaxNum 20 //图的最大顶点数
#define MaxValue 65535 //最大值
int path[MaxNum]; //两点经过的顶点集合的数组
int tmpvertex[MaxNum]; //最短路径的起始点集合
#define USED 0; //已选用顶点
#define NoL -1 //非邻接顶点
typedef struct
{
char Vertex[MaxNum]; //保存顶点信息(序号或字母)
int GType; //图的类型(0:无向图,1)
int VertexNum; //顶点的数量
int EdgeNum; //边的数量
int EdgeWeight[MaxNum][MaxNum]; //保存边的权
int isTrav[MaxNum]; //定义邻接矩阵图结构
}GraphMatrix;
void CreateGraph(GraphMatrix *GM)
{
int i,j,k;
int weight;
char EstartV,EendV;
std::cout<<"输入图中各顶点信息\n";
for (i = 0; i<GM->VertexNum; i++)
{
getchar();
printf("第%d个顶点:",i+1);
scanf("%c",&(GM->Vertex[i]));
}
std::cout<<"输入构成各边的顶点及权值:\n";
for (k = 0; k <GM->EdgeNum; k++)
{
getchar();
printf("第%d条边:",k+1);
scanf("%c %c %d",&EstartV,&EendV,&weight);
for ( i= 0; EstartV != GM->Vertex[i]; i++);
for ( j= 0; EendV != GM->Vertex[j]; j++);
GM->EdgeWeight[i][j] = weight;
if (GM->GType == 0) //若是无向图
{
GM->EdgeWeight[j][i] = weight;
}
}
}
void ClearGraph(GraphMatrix *GM)
{
int i,j;
for (i=0; i<GM->VertexNum; i++)
{
for (j=0; j<GM->VertexNum; j++)
{
GM->EdgeWeight[i][j] = MaxValue;
}
}
}
void OutGraph(GraphMatrix *GM)
{
int i,j;
for (j=0; j<GM->VertexNum; j++)
{
printf("\t%c",GM->Vertex[j]);
}
std::cout<<"\n";
for (i=0; i<GM->VertexNum; i++)
{
printf("%c",GM->Vertex[i]);
for (j=0; j<GM->VertexNum; j++)
{
if (GM->EdgeWeight[i][j] == MaxValue)
{
printf("\tZ"); //以Z表示无穷大
}
else
{
printf("\t%d",GM->EdgeWeight[i][j]); //输出边的权值
}
}
std::cout<<"\n";
}
}
void DisMin(GraphMatrix GM,int vend) //最短路径算法
{
int weight[MaxNum]; //某终止点到各顶点的最短路径长度
int i,j,k,min;
vend--;
for (i =0; i<GM.VertexNum; i++) //初始weight数组
{
weight[i] = GM.EdgeWeight[vend][i]; //保存最小权值
}
for (i=0; i<GM.VertexNum; i++) //初始path数组
{
if (weight[i]<MaxValue && weight[i]>0) //有效权值
{
path[i] = vend; //保存边
}
}
for (i=0; i<GM.VertexNum; i++) //初始tmpvertex数组
{
tmpvertex[i] = 0; //初始化顶点集合为空
}
tmpvertex[vend] = 1; //选入顶点vend
weight[vend] = 0;
for (i = 0; i<GM.VertexNum; i++)
{
min = MaxValue;
k = vend;
for (j=0; j < GM.VertexNum; j++)
{
if (tmpvertex[j] == 0 && weight[j] <min) //查找未用顶点的最小权值
{
min = weight[j];
k = j;
}
}
tmpvertex[k] = 1; //将顶点k选入
for (j = 0; j<GM.VertexNum; j++)
{
if (tmpvertex[j] == 0 && weight[k] + GM.EdgeWeight[k][j] < weight[j])
{
weight[j] = weight[k] + GM.EdgeWeight[k][j];
path[j] = k;
}
}
}
}
int main(int argc, const char * argv[])
{
// std::cout << "Hello, World!\n";
GraphMatrix GM;
char again;
int vend;
int i,k;
printf("求解最短路径问题!\n");
printf("请先输入生成图的类型:");
scanf("%d",&GM.GType);
printf("输入图的顶点数量:");
scanf("%d",&GM.VertexNum);
printf("输入图的边数量:");
scanf("%d",&GM.EdgeNum);
ClearGraph(&GM);
CreateGraph(&GM);
printf("\n请输入结束点:");
scanf("%d",&vend);
DisMin(GM, vend);
vend--;
printf("\n各顶点到达顶点%c的最短路径分别为(起始点-结束点):\n",GM.Vertex[vend]);
for (i = 0; i <GM.VertexNum; i++)
{
if (tmpvertex[i] == 1)
{
k = i;
while (k != vend)
{
printf("顶点%c-",GM.Vertex[k]);
k = path[k];
}
printf("顶点%c\n",GM.Vertex[k]);
}
else
{
printf("%c - %c:无路径\n",GM.Vertex[i],GM.Vertex[vend]);
}
}
printf("\n");
return 0;
}
演示结果:
演示效果图:
参考书籍:《C/C++常用算法手册》