本文已参与「新人创作礼」活动,一起开启掘金创作之路。
算法步骤 (实际应用中不用Prim算稀疏图)
邻接矩阵图
/*定义边*/
typedef struct ENode{
int v1, v2;
int Weight;
} * Edge;
/*邻接矩阵存储图*/
typedef struct MGNode
{
int Nv, Ne;
int V[MaxV][MaxV];
string Data[MaxV];
} * MGraph;
/*图的初始化*/
MGraph CreateMGraph(int Nv)
{
MGraph G = new MGNode();
G->Nv = Nv;
for (int i = 0; i < G->Nv; i++)
for (int j = 0; j < G->Nv; j++)
G->V[i][j] = INF;
return G;
}
/*边的插入*/
void InsertEdge(MGraph G, Edge E) //无向图要插入两次
{
G->V[E->v1][E->v2] = E->Weight;
G->V[E->v2][E->v1] = E->Weight;
}
/*建造一个图*/
MGraph BuildGraph()
{
int Nv;
cin >> Nv;
MGraph G = CreateMGraph(Nv);
cin >> G->Ne;
Edge E = new ENode;
for (int i = 0; i < G->Ne; i++)
{
cin >> E->v1 >> E->v2 >> E->Weight;
InsertEdge(G, E);
}
delete E;
return G;
}
索引最小堆
typedef struct AssNode{
int dist[MaxV];
int reverse[MaxV];
} * AssArray;
//Index[i] = j;
//reverse[j] = i;
//reverse[Index[i]] = i; //赋值
typedef struct IndexHNode{
int *Index; //堆真正存的数据是索引,但比较时用索引对应的dist[Index]
AssArray Ass;
int Size;
int Capacity;
} * IndexHeap;
void SwapIndex(IndexHeap H, int i) //修改反索引数组
{
H->Ass->reverse[H->Index[i]] = i;
}
IndexHeap CreateHeap(int MaxSize)
{
IndexHeap H = new IndexHNode;
H->Size = 0; H->Capacity = MaxSize;
H->Index = new int[MaxSize + 1](); H->Index[0] = 0; //哨兵dist[index[0]] = 0
H->Ass = new AssNode;
return H;
}
/*插入顶点编号为v的顶点, 插入一个Index*/
void InsertIndex(IndexHeap H, int v) //从堆内访问dist必须通过dist[Index]
{
int x = H->Ass->dist[v];
int child;
for ( child = ++H->Size; x < H->Ass->dist[H->Index[child / 2]]; child /= 2) //目的是确定x位置
{
H->Index[child] = H->Index[child / 2];
SwapIndex(H, child);
}
H->Index[child] = v;
SwapIndex(H, child);
}
/*删除并返回最小边的索引*/
int DeleteIndex(IndexHeap H)
{
int v = H->Index[1];
int x = H->Ass->dist[H->Index[H->Size--]];
int parent, child;
for ( parent = 1; parent * 2 <= H->Size; parent = child)
{
child = parent * 2;
if(H->Ass->dist[H->Index[child + 1]] < H->Ass->dist[H->Index[child]] && child != H->Size)
child++;
if(x > H->Ass->dist[H->Index[child]])
{
H->Index[parent] = H->Index[child];
SwapIndex(H, parent);
}
else
break;
}
H->Index[parent] = H->Index[H->Size + 1];
SwapIndex(H, parent);
return v;
}
/*修改dist[v]减少为x, 并调整堆*/
void Fix_up(IndexHeap H, int v, int x)
{
H->Ass->dist[v] = x;
int child;
//切记!!!!!!!!!!!!!!!!!!! 这里使用reverse来访问堆!!!!而不是直接用v!!!(被坑傻过!!!)
for (child = H->Ass->reverse[v]; x < H->Ass->dist[H->Index[child / 2]]; child /= 2)
{
H->Index[child] = H->Index[child / 2];
SwapIndex(H, child);
}
H->Index[child] = v;
SwapIndex(H, child);
}
Prim算法
int Prim(MGraph G, IndexHeap H)
{
int MinTree = 0;
for (int i = 1; i < G->Nv; i++)
H->Ass->dist[i] = G->V[0][i];
H->Ass->dist[0] = 0; //表示0顶点已经是树中一部分
for (int i = 1; i < G->Nv; i++) //除0以外,所有顶点加入索引堆,表示还不是生成树内的顶点
InsertIndex(H, i);
while (H->Size != 0) //队列中都是未加入生成树的顶点
{
int v = DeleteIndex(H); //dist[v]最小,v加入生成树
MinTree += H->Ass->dist[v]; //统计权重
H->Ass->dist[v] = 0; //标记该顶点已加入生成树
for (int i = 0; i < G->Nv; i++)
if(H->Ass->dist[i] != 0 && G->V[v][i] < INF)
if(G->V[v][i] < H->Ass->dist[i])
Fix_up(H, i, G->V[v][i]); //找到最小dist后,在索引优先队列中更新dist
}
return MinTree;
}
int main()
{
MGraph G = BuildGraph();
IndexHeap H = CreateHeap(G->Nv);
cout << Prim(G, H) << endl;
system("pause");
return 0;
}
input:
10 17
0 2 5
0 1 8
1 4 5
1 2 5
1 5 4
4 5 4
2 3 8
2 6 4
5 6 4
5 8 2
3 6 7
6 8 3
3 7 5
6 7 5
6 9 6
8 9 7
7 9 6
output:
38
生成树的具体边,可以加一个parent数组,然后每次找到一条边都插入一个新的图中,我就不写了。。。
下面是不采用优先队列的暴力算法
```cpp
#include <iostream>
#include <cstring>
#include <climits>
using namespace std;
const int MaxV = 100;
/*边的定义*/
typedef struct ENode
{
int v1, v2;
int Weight;
} * Edge;
/*邻接矩阵存储图*/
typedef struct MGNode
{
int Nv, Ne;
int F[MaxV][MaxV];
string Data[MaxV];
} * MGraph;
/*图的初始化*/
MGraph CreateMGraph(int Nv)
{
MGraph G = new MGNode();
G->Nv = Nv;
for (int i = 0; i < G->Nv; i++)
for (int j = 0; j < G->Nv; j++)
G->F[i][j] = INT_MAX;
return G;
}
/*边的插入*/
void InsertEdge(MGraph G, Edge E) //无向图要插入两次
{
G->F[E->v1][E->v2] = E->Weight;
G->F[E->v2][E->v1] = E->Weight;
}
/*建造一个图*/
MGraph BuildGraph()
{
int Nv;
cin >> Nv;
MGraph G = CreateMGraph(Nv);
cin >> G->Ne;
Edge E = new ENode;
for (int i = 0; i < G->Ne; i++)
{
cin >> E->v1 >> E->v2 >> E->Weight;
InsertEdge(G, E);
}
delete E;
return G;
}
/*邻接表图存生成的MST*/
struct AdjVNode
{
int AdjV;
int Weight;
AdjVNode *Next;
};
typedef struct VNode
{
AdjVNode *EdgeFirst;
string Data;
} AdjList[MaxV];
typedef struct LGNode
{
int Nv, Ne;
AdjList L;
} * LGraph;
/*MST树初始化*/
LGraph CreateLGraph(int Nv)
{
LGraph MST = new LGNode();
MST->Nv = Nv;
return MST;
}
/*MST边的插入*/
void InsertEdgeMST(LGraph MST, Edge E)
{
AdjVNode *V = new AdjVNode;
V->AdjV = E->v2;
V->Weight = E->Weight;
V->Next = MST->L[E->v1].EdgeFirst;
MST->L[E->v1].EdgeFirst = V;
//无向图,插入两条边
V = new AdjVNode;
V->AdjV = E->v1;
V->Weight = E->Weight;
V->Next = MST->L[E->v2].EdgeFirst;
MST->L[E->v2].EdgeFirst = V;
}
int FindMinDist(MGraph G, int dist[])
{
int MinDist = INT_MAX;
int MinV;
for (int i = 0; i < G->Nv; i++)
if(dist[i] != 0 && dist[i] < MinDist)
{
MinDist = dist[i];
MinV = i;
}
if(MinDist < INT_MAX)
return MinV;
else
return -1;
}
int Prim(MGraph G, LGraph MST)
{
int dist[MaxV], TotalWeight = 0;
int parent[MaxV], V, W;
int VCount = 0;
Edge E = new ENode;
memset(parent, 0, sizeof(parent));
for (int i = 1; i < G->Nv; i++)
dist[i] = G->F[0][i];
dist[0] = 0;
VCount++;
parent[0] = -1;
while (1)
{
V = FindMinDist(G, dist);
if(V == -1)
break;
E->v1 = parent[V];
E->v2 = V;
E->Weight = dist[V];
InsertEdgeMST(MST, E);
TotalWeight += dist[V];
dist[V] = 0;
VCount++;
for (int i = 0; i < G->Nv; i++)
{
if(dist[i] != 0 && G->F[V][i] < INT_MAX)
if(G->F[V][i] < dist[i])
{
dist[i] = G->F[V][i];
parent[i] = V;
}
}
}
if(VCount < G->Nv)
TotalWeight = -1;
return TotalWeight;
}
int main()
{
MGraph G = BuildGraph();
LGraph MST = CreateLGraph(G->Nv);
cout << Prim(G, MST) << endl;
AdjVNode *V;
for (int i = 0; i < MST->Nv; i++)
{
V = MST->L[i].EdgeFirst;
cout << i << "->";
while (V)
{
cout << V->AdjV << "->";
V = V->Next;
}
cout << "NULL" << endl;
}
system("pause");
return 0;
}
结果