#include<iostream>
#include<time.h>
#include<Windows.h>
#include< queue>
using namespace std;
#define NUM 100
#pragma warning(disable:4996)
typedef struct link
{
int index;
int distance;
struct link *next;
}*Node, node;
typedef struct Link
{
char word;
Node first;
}*Head, head;
typedef struct g
{
head linktable[NUM];
int n, e;
}*Map, map;
void Allocatemap(Map &G);//给图指针分配一块图的专属区域
void Parmmap(Map G);//传入邻接表参数
void Createmap(Map G);//创建邻接表表头
int Locatev(Map G, char x);//确定关系顶点的位置
void Relatemap(Map G);//创建图的关系链
void Printmap(Map G);//打印图的邻接表
void Findminpath(Map G, int v[], int visited[]);//寻找已经确定结点中所有相邻结点中非环路的最短路径
void Prim(Map G, int v[], int visited[]);//Prim算法
void Findmintree(Map G, Map M, int visited[]);//寻找最小的树枝
int Correctpath(Map M, int i, int j); //进行合格路线的判定,满足两个条件即为合格:①未重复选取②未构成环路
void CreateMap(Map M, int i, int j);//生成树的插入
void Kruskal(Map G, int visited[]);//Kruskal算法
int main()
{
int visited[NUM] = { 0 };
int v[NUM] ;
memset(v, -1, sizeof(v));//v数组是Prim算法的记录工具,全部赋值为-1是因为存放邻接表的数组下标是从0开始的
Map G;
Allocatemap(G);
Parmmap(G);
Createmap(G);
Relatemap(G);
Printmap(G);
Prim(G, v, visited);
Kruskal(G, visited);
return 0;
}
void Allocatemap(Map &G)//给图指针分配一块图的专属区域
{
G = new map;
}
void Parmmap(Map G)//传入邻接表参数
{
cout << "请输入图的基本参数,即图的顶点数和边数:";
cin >> G->n;
cin >> G->e;
}
void Createmap(Map G)//创建邻接表表头
{
char a[NUM];
cout << "请输入顶点的具体数据:";
cin >> a;
for (int i = 0; i < G->n; i++)
{
G->linktable[i].word = a[i];
G->linktable[i].first = NULL;
}
}
int Locatev(Map G, char x)//确定关系顶点的位置
{
int i;
for (i = 0; i < G->n; i++)
{
if (G->linktable[i].word == x) return i;
}
return -1;
}
void Relatemap(Map G)//创建图的关系链
{
int j = 0, k = 0;
char a, b;
int c = 0;
cout << "请输入图的关系以及相应带权:" << endl;
for (int i = 0; i < G->e; i++)
{
cin >> a >> b >> c;
j = Locatev(G, a);
k = Locatev(G, b);
Node p = new node;
p->index = k;
p->distance = c;
p->next = G->linktable[j].first;
G->linktable[j].first = p;
}
}
void Printmap(Map G)//打印图的邻接表
{
cout << "图的邻接表为:" << endl;
for (int i = 0; i < G->n; i++)
{
Node p = G->linktable[i].first;
cout << G->linktable[i].word << "--->";
while (p)
{
cout << G->linktable[p->index].word << " ";
cout << p->distance;
p = p->next;
if (p)cout << "-->";
}
cout << endl;
}
}
void Findminpath(Map G, int v[],int visited[])//寻找已经确定结点中所有相邻结点中非环路的最短路径
{
Node p;
int outset=0;
int end=0;
int i = 0;
int min = INT_MAX;
do
{
p = G->linktable[v[i]].first;
while (p)
{
if (p->distance < min && visited[p->index]==0)
{
min = p->distance;
end = p->index;
outset = v[i];
}
p = p->next;
}
i++;
} while (v[i] != -1);
v[i] = end;
visited[end] = 1;
cout << G->linktable[outset].word << "--->" << G->linktable[end].word <<" "<<min<< endl;
}
void Prim(Map G, int v[],int visited[])//Prim算法
{
char a;
cout << "请选择Prim算法的开始顶点:";
cin >> a;
visited[Locatev(G, a)] = 1;
v[0] = Locatev(G, a);
cout << "Prim算法求得最小生成树为:" << endl;
while (v[G->n - 1] == -1)
Findminpath(G, v, visited);
}
void Findmintree(Map G,Map M,int visited[])//寻找最小的树枝
{
Node p;
int outset = 0;
int end = 0;
int min = INT_MAX;
for(int i=0;i<G->n;i++)
{
p = G->linktable[i].first;
while (p)//选取最短路径且不构成环路的线路
{
if (p->distance < min&&Correctpath(M, p->index,i) == 0)
{
min = p->distance;
end = p->index;
outset = i;
}
p = p->next;
}
}
visited[outset] = 1;
visited[end] = 1;
CreateMap(M, outset, end);
CreateMap(M, end, outset);
cout << G->linktable[outset].word << "--->" << G->linktable[end].word << " " << min << endl;
}
int Correctpath(Map M,int i,int j)//进行合格路线的判定,满足两个条件即为合格:①未重复选取②未构成环路
{
Node p = M->linktable[i].first;
Node q = M->linktable[j].first;
while (p)
{
while (q)
{
if (q->index == p->index)return -1;
else if(q->index == i || p->index == j)return -1;
q = q->next;
}
p = p->next;
}
return 0;
}
void CreateMap(Map M, int i, int j)//生成树的插入
{
Node p = new node;
p->index = j;
p->next = M->linktable[i].first;
M->linktable[i].first = p;
}
void Kruskal(Map G, int visited[])//Kruskal算法
{
Map M;
Allocatemap(M);
for(int i=0;i<G->n;i++)
M->linktable[i].first = NULL; //创建一个存储生成树的邻接表并初始化
cout << "Kruskal算法求得最小生成树为:" << endl;
for(int i=0;i<G->n-1;i++)
Findmintree(G, M,visited);
}
上图左图是无向图演示示例的逻辑结构图,右图是对应图的邻接表
【小结】:①Prim算法求解无向图的核心思想是每一次寻找当前已确定结点与其他相邻结点的最短路径。与迪杰斯特拉算法求最短路径的核心思想“贪心”有异曲同工之妙,此算法虽然时间复杂度较高,但不用每次进行是否构成回路的判断,因为构成回路的前提就是下一个连接的结点一定在之前已经被访问过。此算法比较适用于求解稠密图的最小生成树。
下图是Prim算法过程演示示意图
②Kruskal算法求解无向图的核心思想是对所有路径长度进行一次排序,排序完成后依次从小到大选取不构成回路的路径,直至所有点被选取完毕。此算法比较适用于求解稀疏图的最小生成树。
下图是Kruskal算法过程演示示意图
下图是对应代码的运行结果