/*
Prim 邻接表+优先队列优化
Kruskal 邻接矩阵+并查级优化,因为Kruskal里没有需要遍历邻接边的操作,所以简单的用邻接矩阵
结果保证:获得路径即可
e因为要有生成树,原图必须是一条连通的路线,所以e>=n-1
*/
#include<iostream>
#include<queue>
#include<Windows.h>
using namespace std;
const int maxnum = 100;
const int inf = 0x3f3f3f3f;
typedef struct AdjNode {
int v;
int w;
struct AdjNode* next;
}AdjNode;
typedef struct VexNode {
AdjNode* first;
}VexNode;
typedef struct ALGraph {
VexNode vexs[maxnum];
int vexnums;
int edgenums;
}ALGraph;
int father[maxnum];
typedef struct edge {
int u;
int v;
int w;
edge(int ui, int vi, int wi)
{
u = ui;
v = vi;
w = wi;
}
edge()
{
}
//运算符重载
friend bool operator >(const edge e1, const edge e2)
{
return e1.w > e2.w;
}
}edge;
edge e[maxnum];
ALGraph Lg;
int vexnums, edgenums;
bool s[maxnum];
//构建邻接矩阵
void CreateALGraph() {
cout << "输入顶点数量和边数量:" << endl;
cin >> vexnums >> edgenums;
cout << "输入对应的边关系:" << endl;
//初始化邻接表的顶点
for (int i = 0; i < vexnums; i++)
{
Lg.vexs[i].first = NULL;
}
//初始化邻接表的边
for (int i = 0; i < edgenums; i++)
{
int a, b, w;
cin >> a >> b >> w;
AdjNode* an = new AdjNode;
an->v = b;
an->w = w;
an->next = Lg.vexs[a].first;
Lg.vexs[a].first = an;
AdjNode* bn = new AdjNode;
bn->v = a;
bn->w = w;
bn->next = Lg.vexs[b].first;
Lg.vexs[b].first = bn;
}
}
void prim1000()
{
CreateALGraph();
int u;
cin >> u;
int t1 = GetTickCount();
for (int z = 0; z < 1000; z++)
{
//重置s集合
for (int i = 1; i <= vexnums; i++)
{
s[i] = false;
}
AdjNode* an;
s[u] = true; //将u顶点加入s集合中
priority_queue<edge, vector<edge>, greater<edge> > queue; //边队列
edge* path = new edge[vexnums - 1]; //最小生成树结果
int k = 0; //记录结果的生成进度
//将u顶点的邻接边都加入队列中
an = Lg.vexs[u].first;
while (an != NULL)
{
queue.push(edge(u, an->v, an->w)); //o(e分*loge)
an = an->next;
}
for (int i = 1;queue.size()>0; i++)
{
//求出v-s内最近顶点
edge e = queue.top();
int t = e.v;
queue.pop();
if (k == vexnums - 1) break; //n-1 总o(n*e分*loge)优先队列
if (s[t]) continue;
s[t] = true; //把顶点加入S中
path[k++] = e;
//更新queue将改变的边重新加入队列中,遍历t的所有邻接边 t->i
an = Lg.vexs[t].first;
while (an != NULL) //o(e)
{
if (!s[an->v])
{
queue.push(edge(t, an->v, an->w)); //e总,在这其中已经排过序的边虽然不会再加入queue中但是还是会遍历一遍,所以边的遍历是2遍,排序一个loge,比Kruskal慢了
}
an = an->next;
}
}
for (int i = 0; i < vexnums - 1; i++)
{
cout << "<" << path[i].u << "," << path[i].v << "> ";
}
cout << endl;
}
int t2 = GetTickCount();
cout << "总计耗时:" << t2 - t1 << endl;
}
void CreateAMGraph()
{
cout << "输入顶点数量和边数量:" << endl;
cin >> vexnums >> edgenums;
//初始化边关系
cout << "输入对应的边关系:" << endl;
for (int i = 0; i < edgenums; i++)
{
int a, b, w;
cin >> a >> b >> w;
e[i] = edge(a, b, w);
}
}
int Find(int x)
{
if (x != father[x])
father[x] = Find(father[x]);
return father[x];
}
int Merge(int a, int b)
{
int p = Find(a);
int q = Find(b);
if (p == q) return 0;
if (p > q)
father[p] = q;//小的赋值给大的集合号
else
father[q] = p;
return 1;
}
int cmp(const void *a, const void *b)
{
edge* e1 = (edge*)a;
edge* e2 = (edge*)b;
return e1->w - e2->w;
}
void Kruskal1000()
{
CreateAMGraph();
int t1 = GetTickCount();
for (int z = 0; z < 1000; z++)
{
int n = vexnums;
edge* path = new edge[vexnums - 1]; //最小生成树结果
int k = 0;
//并查集初始化与邻近集合初始化
for (int i = 1; i <= vexnums; i++)
{
father[i] = i;
}
//对边进行排序
qsort(e, edgenums, sizeof(e[0]), cmp); //o(eloge) 一次排序完成
for (int i = 0; i < edgenums; i++) //n-1
if (Merge(e[i].u, e[i].v) /*logn*/) //-->n*logn 从小到大
{
path[k++] = e[i];
n--;
if (n == 1)
{
break;
}
}
for (int i = 0; i < vexnums-1; i++)
{
cout << "<" << path[i].u << "," << path[i].v << "> ";
}
cout << endl;
}
int t2 = GetTickCount();
cout << "总计耗时:" << t2 - t1 << endl;
}
int main()
{
prim1000();
Kruskal1000();
system("pause");
return 0;
}
测试数据
7 12
1 2 23
1 6 28
1 7 36
2 3 20
2 7 1
3 7 4
3 4 15
4 7 9
4 5 3
5 7 16
5 6 17
6 7 25
Kruskal比Prim快多了,无脑用Kruskal算了