最小生成树Prim算法(邻接矩阵)(C++实现)
实现代码
/*
author : eclipse
email : [email protected]
time : Mon Apr 13 20:13:55 2020
*/
#include<bits/stdc++.h>
using namespace std;
vector< vector<int> > matrix;
vector<vector<int> > minSpanningTree;
int weight = 0;
void createNet(vector< pair< pair<int,int>, int> > net){
for(int i = 0; i < net.size(); i++){
matrix[net[i].first.first - 1][net[i].first.second - 1] = net[i].second;
matrix[net[i].first.second - 1][net[i].first.first - 1] = net[i].second;
}
}
void prim(int start) {
start--;
int vexNum = matrix.size();
vector<bool> tag;
vector< pair<int, int> > distance;//维护第i个点的最小代价边其该边所连接的邻接点
//first为第i个顶点的最小代价边权值
//second为第i个顶点最小代价边邻接点
tag.resize(vexNum);//设置标志表示该顶点是否已经加入最小生成树
distance.resize(vexNum);
for (int i = 0; i < tag.size(); i++) {
tag[i] = false;
distance[i].first = INT_MAX;
}
tag[start] = true;
for (int i = 0; i < vexNum; i++) {
if (matrix[start][i] < distance[start].first && !tag[i]) {
distance[start].first = matrix[start][i];
distance[start].second = i;
}
}
int treeNode = 1;
while (treeNode != vexNum) {
int minIndex = 0;
int minTo;
for (int i = 0; i < vexNum; i++) {
//选取树上所有节点最小代价边的最小代价边
if (distance[minIndex].first > distance[i].first && tag[i]) {
minIndex = i;
}
}
minTo = distance[minIndex].second;
minSpanningTree[minIndex].push_back(minTo);
weight += matrix[minIndex][minTo];//累加权值
tag[minTo] = true;//加入最小生成树
for (int i = 0; i < vexNum; i++) {
// 维护所有顶点的最小代价边
distance[i].first = INT_MAX;
for (int j = 0; j < vexNum; j++) {
if (matrix[i][j] < distance[i].first && !tag[j]) {
distance[i].first = matrix[i][j];
distance[i].second = j;
}
}
}
treeNode++;
}
}
void levelTraverse(int start) {
queue<int> q;
start--;
q.push(start);
while (!q.empty()) {
printf("%d ", q.front() + 1);
int front = q.front();
for (int i = 0; i < minSpanningTree[front].size() ; i++) {
q.push(minSpanningTree[front][i]);
}
q.pop();
}
}
int main(int argc, char const *argv[])
{
int N;
while (~scanf("%d", &N)) {
// 初始化并建立无向网~~
int vexNum = 0;
vector< pair< pair<int,int>, int> > net;
net.resize(N);
for (int i = 0; i < N; i++) {
int u, v, value;
scanf("%d%d%d", &net[i].first.first, &net[i].first.second, &net[i].second);
vexNum = max(vexNum, net[i].first.first);
vexNum = max(vexNum, net[i].first.second);
}
matrix.resize(vexNum);
minSpanningTree.resize(vexNum);
for (int i = 0; i < vexNum; i++) {
matrix[i].resize(vexNum);
for (int j = 0; j < vexNum; j++) {
matrix[i][j] = INT_MAX;
}
}
createNet(net);
//~~
prim(1);
printf("%d\n", weight);
levelTraverse(1);
}
return 0;
}
算法思路
- 从图的起点开始,将起点加入最小生成树,从树上所有点的邻接点选取与其具有最小代价边的邻接点加入最小生成树,直到将整张图遍历完成
- 用distance数组维护第i个点的最小代价边其该边所连接的邻接点
- 较为详细的说明请查看上述代码的注释
- 问题规模n下,时间复杂度O(n ^ 3)
伪代码
viod Prim(G, T) {
T = Ø;//初始化空树
U = {w};//添加任意顶点w
while((V - U) != Ø){//若树中不含有全部顶点
设 (u, v) 是使u ∈ U 且 v ∈ (V - U) 且权值最小的边
T = T ∪ {u,v};//边归入树
U = U ∪ {v};//顶点归入树
}
- 上述伪代码来自王道考研数据结构
样例图解
- 无向网
- Prim构造最小生成树过程
- 上述示例参考王道考研数据结构
测试数据
10
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6
输出结果
15
1 3 6 2 4 5
鸣谢
感谢王道论坛及其产品提供的宝贵建议和测试样例
最后
- 上述实现没有对选取最小代价边的算法进行优化,而导致了O(n ^ 3)的时间复杂度,而Prim算法时间复杂度一般为O(n ^ 2)
- 由于博主水平有限,不免有疏漏之处,欢迎读者随时批评指正,以免造成不必要的误解!