一、最小生成树和最短路径的区别
最小生成树是指生成树各边的权值总和最小的树,保证整体树的权值是最小,并不保证任意两点间的权值最小。
最短路径是保证从起始点到指定其余点的权值和最小,即求从起点到其余各点路径上权值和最小的路径。
二、3种主要算法实现
#if ! defined(MGRAPH_H)
#define MGRAPH_H
#include<limits.h>
#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 50
typedef char VertexType;//顶点类型
typedef int Adjmatrix;//边的权值
typedef struct{
VertexType vexs[MaxVertexNum];
Adjmatrix arcs[MaxVertexNum][MaxVertexNum];//有边为指定数值,没有为INT_MAX
}MGraph;
/********Prim算法********
基本思路:
首先,选中指定1个顶点作为源点。
然后,选择从该顶底到其余顶点中权值最小的点,并加入到源点中。
再次,从源点中选择到其余顶点中权值最小的点。重复以上过程。
*/
typedef int VRType;//对应顶点在矩阵中的索引
typedef enum{FREE,USED} State;//辅助顶点的状态
typedef struct{
VertexType ver;//顶点类型
VRType lowcost;//边的权重
State used;//取值为FREE,USED
}minedge;//从某个顶点到其余边的最小权重
typedef minedge Edges[MaxVertexNum];
//获取顶点在G中的下标
int VtxNum(MGraph G,VertexType u,int n){
//G中有顶点和边的数组,u顶点,n顶点总数
int i;
for(i=0;i<n;i++){
if(G.vexs[i]==u){
return i;
}
}
return -1;
}
int minEdge(Edges edges,int n){//min是系统函数
//获取最小值
int i;
int min=-1;//min是lowcost的最小值索引
int temp=0;
int templowcost;
for(i=0;i<n;i++){
if(edges[i].used==FREE){
if(temp==0){
//获取第一个最小值
temp=1;
templowcost=edges[i].lowcost;
min=i;
}else if(temp==1){
//获取最小的
if(edges[i].lowcost<templowcost){
templowcost=edges[i].lowcost;
min=i;
}//end if
}//end if
}
}//end for
return min;
}
//获取
void Prim(MGraph G,VertexType u,int n){
//G矩阵表,u起始顶点,n顶点总数
int k,v,j,pre;
Edges edges;//定义辅助边数组
k=VtxNum(G,u,n);//获取顶点u在辅助数组中的位置
//辅助数组初始化
for(v=0;v<n;v++){
if(v!=k){
edges[v].ver=u;//各辅助数组记录各种点到u点的权重
edges[v].lowcost=G.arcs[k][v];
edges[v].used=FREE;
}else{
//本节点
edges[v].lowcost=0;
edges[v].used=USED;
}
}
for(j=1;j<n;j++){
k=minEdge(edges,n);//k是u到其他点最小的权重边对应的点
//******显示******//
printf("v%c-v%c\n",edges[k].ver,G.vexs[k]);
edges[k].used=USED;//第k个顶点并入U
for(v=0;v<n;v++){
if(edges[v].used==FREE && G.arcs[k][v]<edges[v].lowcost){
//各点到k节点,所在边的小权重
edges[v].ver=G.vexs[k];
edges[v].lowcost=G.arcs[k][v];
}
}//end for
}//end for
}
/*******Kruskal算法
基本思路:选择最小的边,判断两个顶点是否已经在生成树中,若没有添加到生成树中;若已经在最小生成书中则放弃这条边。
1.对边排序,按照。
2.选择最小的边。
3.判断边的两点是否已经在最小生成树中。
4.重复2和3。
*******/
typedef struct{
int v1;//第一个顶点
int v2;//第二个顶点
int lowcost;//两点之间边的权重
}Edge;//Kruskal辅助边算法
typedef Edge KEdges[MaxVertexNum*MaxVertexNum];//辅助数组
void KSort(KEdges KE,int n){
//对辅助数组排序
Edge e;
int i,j,m=n*n;
//直接插入排序算法
for(i=1;i<m;i++){
if(KE[i].lowcost<KE[i-1].lowcost){
e=KE[i];
j=i;
do{
KE[j]=KE[j-1];//往后移位
j--;
}while(j-1>=0 && e.lowcost<KE[j-1].lowcost );//这个条件和if的差不多
KE[j]=e;
}
}
}
void Kruskal(MGraph G,int n){
KEdges kedges;//辅助数组
int vectextag[MaxVertexNum]={0};//顶点出现次数标记
int i,j,k=0,m;
//1.初始化辅助数组
for(i=0;i<n;i++){
for(j=0;j<n;j++){
kedges[k].v1=i;
kedges[k].v2=j;
kedges[k].lowcost=G.arcs[i][j];
k++;
}
}//end for
//2.对边数组排序
KSort(kedges,n);
//3.生成树
k=1;//用来标记相应顶点出现次数
m=n*n;//边的条数
for(i=0;i<m;i++){//不能简单适用i+=2于无向图,虽然因无向图边(i,j)和(j,i)的权值是相同的,但也有可能其他边的权值是相同的。
if(INT_MAX == kedges[i].lowcost) continue;//如果边是无穷大就不需要执行加入树的操作
if(0 == vectextag[kedges[i].v1] && 0== vectextag[kedges[i].v2]){
//新边两个新顶点
vectextag[kedges[i].v1]=k;
vectextag[kedges[i].v2]=k;
k++;
}else if(0== vectextag[kedges[i].v1] && 0!=vectextag[kedges[i].v2]){
//v2顶点已经在最小生成树中
vectextag[kedges[i].v1]=vectextag[kedges[i].v2];//v2顶点所在树标志赋予v1顶点。
}else if(0!= vectextag[kedges[i].v1] && 0==vectextag[kedges[i].v2]){
vectextag[kedges[i].v2]=vectextag[kedges[i].v1];//v1顶点所在树标志赋予v2顶点。
}else{
//没有新节点,当2个顶点标志相同时说明它们已经在同一颗树
if(vectextag[kedges[i].v1] == vectextag[kedges[i].v2]) continue;
//不同的小生成树,修改为同一棵生成树
for(j=0;j<n;j++){
if(vectextag[j] == vectextag[kedges[i].v1]){
//把等于v1的修改为顶点v2的点
vectextag[j]=vectextag[kedges[i].v2];
}
}//end for
}//end if
//***访问边***//
printf("v%c-v%c\n",G.vexs[kedges[i].v1],G.vexs[kedges[i].v2]);
}//end for 遍历边
}
/*******Dijkstra求最短路径
目标:求指定起点到其他各顶点的最短路径和权重。
基本思路:
1.求指定点到其他顶点的权重。
2.把各个点分别加入到其中时,比较权重之和。
3.若小于原先的顶点,则替换新值
********/
void Dijkstra(MGraph G,int v0,int n){
int Distance[MaxVertexNum];//指定点到各顶点的权重。
int Path[MaxVertexNum]={-1};//Path的数值是由近到远。
int S[MaxVertexNum];//1表示顶点Source已经在源中了,0表示没有。
int v,i,min,j;
//1.初始化权重、路径和是否在源中
for(v=0;v<n;v++){
S[v]=0;//是否在源中
Distance[v]=G.arcs[v0][v];//权重
if(Distance[v]<INT_MAX){
//有边
Path[v]=v0;//父节点为v0
}
}
Distance[v0]=0;//源点到自身的距离为0
S[v0]=1;//把v0点加入到源点中
//2.生成最短路径树
for(i=1;i<n;i++){//i=1是应为v0已经在源点的集合中,剩余n-1条边
min=INT_MAX;//最小的权重
for(j=0;j<n;j++){
if(!S[j] && Distance[j]<min){
v=j;//v0到其他顶点的最小权重
min=Distance[j];
}
}
S[v]=1;//把该顶点的最小权重边添加到源中
//更新权重表和路径
for(j=0;j<n;j++){
if( !S[j] &&
!(Distance[v]==INT_MAX || G.arcs[v][j]==INT_MAX) && //左边不能有INT_MAX,如果有会上溢
(Distance[v]+G.arcs[v][j]<Distance[j])){//走新节点v比原来的小
Distance[j]=Distance[v]+G.arcs[v][j];//更改为更小的路线
Path[j]=v;//j的父节点变成v
}
}//end for
}//end for
//3.访问路径
for(i=0;i<n;i++){
printf("v%c-(%d)-v%c\n",G.vexs[v0],Distance[i],G.vexs[i]);//显示v0到
}
}
int main(){
void PrimTest();
void DijkstraTest();
PrimTest();
DijkstraTest();
return 0;
}
void PrimTest(){
MGraph G1={
{'1','2','3','4','5','6'},//顶点
{//矩阵
{INT_MAX,6,1,5,INT_MAX,INT_MAX},//1到其他各个顶点
{6,INT_MAX,5,INT_MAX,3,INT_MAX},//2到其他各个顶点
{1,5,INT_MAX,5,6,4},//3到其他各个顶点
{5,INT_MAX,5,INT_MAX,INT_MAX,2},
{INT_MAX,3,6,INT_MAX,INT_MAX,6},
{INT_MAX,INT_MAX,4,2,6,INT_MAX}
}
};
Prim(G1,'1',6);
printf("\n\n");
Kruskal(G1,6);
printf("\n\n");
}
void DijkstraTest(){
MGraph G2={
{'1','2','3','4','5','6'},//顶点
{//矩阵
// 1 2 3 4 5 6
{INT_MAX, 3 ,INT_MAX, 20 ,INT_MAX,INT_MAX},//1到其他各个顶点
{INT_MAX,INT_MAX, 2 ,INT_MAX, 4 ,INT_MAX},//2到其他各个顶点
{INT_MAX,INT_MAX,INT_MAX, 7 , 1 , 5 },//3到其他各个顶点
{INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX},
{INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX},
{INT_MAX,INT_MAX, 3 ,INT_MAX,INT_MAX,INT_MAX}
}
};
Dijkstra(G2,0,6);
}
#endif