Prim算法的原理见《算法笔记》p406
下面来说说用代码怎么实现这个思路。
首先,需要准备一个数组来记录一个每个顶点是在保护罩内(true)还是在保护罩外(false)。注意,这里并不需要两个数组(一个存所有在保护罩内的顶点,一个存所有在保护罩外的顶点,这样太麻烦了)。
然后需要准备一个数组来记录所有顶点到保护罩的距离(weight),注意只要距离不是INFINETE,就说明一定能从保护罩出发到这个顶点。
Prim算法生成最小生成树的过程说白了就是一个while(1)循环
用一个临时变量pos来记录亚历山大大帝的当前的位置。
然后只需要更新pos能到所有顶点的weight就行了。(只需要更新 pos所连接的那些点就行了!因为每次循环只有这些顶点的权重可能发生变化。)
更新完weight之后,只 需要!选择不在保护罩中的 最小的weight点 走就行!即更新pos为该点.(注意不需要判断一个顶点是不和保护罩有边!因为weight!=INF 就代表一定能从保护罩走到该点)
用一个循环遍历“记录是否在保护罩”的数组就可以。设置一个temp 一个minWeight,遍历数组中所有的元素。不断更新这两个值就可以,然后pos = temp。 如果循环下来temp没变,说明当前已经无路可走了,跳出while循环即可。
代码:
这些是准备工作:可以跳过不看
// 图的基本算法与Prim算法实现.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include <algorithm>
#include <map>
#include <queue>
#include <string>
using namespace std;
#define INF 10000
class graph {
int verNum;
int edgNum;
char* ver;
int** edg;
map<char, int> verMap;
queue<string> minGeneTree;
int minTreeWeight;
bool* isIn;
int* verWeight;
public:
void generate();
void display();
void Prim();
void displayTree();
};
void graph::displayTree() {
cout << "最小生成树的权重:" << minTreeWeight << endl;
cout << "最小生成树:" << endl;
while (!minGeneTree.empty()) {
cout << minGeneTree.front() << endl;
minGeneTree.pop();
}
return;
}
void graph::generate() {
cout << "请输入顶点数:";
cin >> verNum;
cout << "请输入边数:";
cin >> edgNum;
#if 1
isIn = (bool*)malloc(sizeof(bool)*verNum);
memset(isIn, 0, sizeof(bool)*verNum);
verWeight = (int*)malloc(sizeof(int)*verNum);
verWeight[0] = 0;
fill(verWeight + 1, verWeight + verNum, INF);
#endif
ver = (char*)malloc(sizeof(char)*verNum);
memset(ver, 0, sizeof(char)*verNum);
edg = (int**)malloc(sizeof(int*)*verNum);
memset(edg, 0, sizeof(int*)*verNum);
for (int i = 0; i < edgNum; i++) {
edg[i] = (int*)malloc(sizeof(int)*verNum);
memset(edg[i], 0, sizeof(int)*verNum);
}
for (int i = 0; i < verNum; i++) {
cout << "请输入第" << i << "个顶点:";
cin >> ver[i];
verMap[ver[i]] = i;
}
for (int i = 0; i < edgNum; i++) {
cout << "请输入第" << i << "条边 例如:a,b:";
char buff[4] = { 0 };
cin >> buff;
int row = verMap[buff[0]];
int col = verMap[buff[2]];
cout << "请输入该边的权重:";
int weight;
cin >> weight;
edg[row][col] = weight;
edg[col][row] = weight;
}
return;
}
void graph::display() {
cout << "邻接矩阵:" << endl;
//cout << "所有顶点:" << endl;
for (int i = 0; i < verNum; i++) {
cout << " " << ver[i];
}
cout << endl;
//cout << "邻接矩阵:" << endl;
for (int i = 0; i < verNum; i++) {
cout << ver[i] << " ";
for (int j = 0; j < verNum; j++) {
cout << edg[i][j] << " ";
}
cout << endl;
}
}
Prim算法的实现:
minTreeWeight是用来累积最小生成树的总权重的。
pos是亚历山大的位置。默认从0开始。即从第一个顶点开始生成最小生成树。
void graph::Prim() {
minTreeWeight = 0;
int pos = 0;
verWeight[0] = 0;
while (1) {
//1.标记pos在保护罩内
isIn[pos] = true;
minTreeWeight += verWeight[pos];
//2.更新pos周围顶点的权重
for (int i = 0; i < verNum; i++) {
if (edg[pos][i] != 0 && isIn[i] == false) {
verWeight[i] = (edg[pos][i] < verWeight[i]) ? edg[pos][i] : verWeight[i];
}
}
//3.寻找最小权重顶点
int min = INF;
int minpos = -1;
for (int i = 0; i < verNum; i++) {
if (isIn[i] == false && verWeight[i] < min) {
minpos = i;
min = verWeight[i];
}
}
//4.如果没有找到,就跳出循环
if (minpos == -1) return;
//5.更新pos的位置
pos = minpos;
//6.以下代码可忽略,这些是用来保存路径的
int prePos;
for (int i = 0; i < verNum; i++) {
if (isIn[i] == true && edg[pos][i] == min) {
prePos = i;
}
}
#if 1
char edgString[1024] = { 0 };
sprintf(edgString, "(%c,%c)", ver[prePos], ver[pos]);
minGeneTree.push(string(edgString));
#endif
}
return;
}
int main()
{
#if 0
#else if
graph G;
G.generate();
G.display();
G.Prim();
G.displayTree();
#endif
return 0;
}