题目描述
根据输入创建无向网。分别用Prim算法和Kruskal算法构建最小生成树。(假设:输入数据的最小生成树唯一)
输入
顶点数n
n个顶点
边数m
m条边信息,格式为:顶点1 顶点2 权值
Prim算法的起点v
输出
输出最小生成树的权值之和
对两种算法,按树的生长顺序,输出边信息(Kruskal中边顶点按数组序号升序输出)
输入样例 输出样例
v1 v2 v3 v4 v5 v6 15
10 prim:
v1 v2 6 v1 v3 1
v1 v3 1 v3 v6 4
v1 v4 5 v6 v4 2
v2 v3 5 v3 v2 5
v2 v5 3 v2 v5 3
v3 v4 5 kruskal:
v3 v5 6 v1 v3 1
v3 v6 4 v4 v6 2
v4 v6 2 v2 v5 3
v5 v6 6 v3 v6 4
v1 v2 v3 56
思考
在做题中,会遇到一些问题。比如在图的邻接矩阵中,0表示的是两个顶点间没有连接,而在Prim算法的lowcost数组中,由于无穷大在C/C++中不好表示,因此用0来表示,此处的0代表的是无穷大(其实也是没有连接的意思),所以要新增一个访问数组visit。
Prim算法实现
void Prim(int start) {
//数组shortEdge
string* adjvex = new string[n];//结点
int* lowcost = new int[n];//最小权值
//存储数组
string* a1 = new string[n];//存储第一个结点
string* a2 = new string[n];//存储第二个结点
int* a3 = new int[n];//存储第一个顶点和第二个顶点间的权值
int count = 0;//计数
int k = 0;//存储数组下标
int m = n - 1;
//初始化
for (int i = 0; i < n; i++) {
adjvex[i] = a[start];
lowcost[i] = matrix[start][i];
}
visit[start] = 1;//访问初始顶点
lowcost[start] = 0;//初始顶点的lowcost值赋0
//循环,不断更新存储值和shortEdge数组值
while (m--) {
int min = 99999;//最小权值数值标记
int flag = 0;//最小权值下标标记
//寻找最小权值
for (int i = 0; i < n; i++) {
//条件就是s2[i]不能等于0且小于min
if (lowcost[i] != 0 && lowcost[i] < min) {
min = lowcost[i];
flag = i;
}
}
//更新存储值
visit[flag] = 1;//visit设置成已访问结点
a1[k] = adjvex[flag];//第一个存储结点存储寻找到的下标对应的s1的值
a2[k] = a[flag];//第二个存储节点存储寻找到的下表对应的a的值(邻接矩阵结点)
a3[k] = min;//存储两者最小权值
count += min;
lowcost[flag] = 0;
k++;
//更新shortEdge数组
for (int i = 0; i < n; i++) {
//条件1:邻接矩阵的值小于lostcost数组的值 且 邻接矩阵的值不等于0
//条件2:lostcost数组的值等于0 且 邻接矩阵的值不等于0
//条件1,2的必要条件:visit数组的值等于0(还未被访问的顶点)
if ((matrix[flag][i] < lowcost[i] && matrix[flag][i] != 0) || (lowcost[i] == 0 && matrix[flag][i] != 0)) {
if (visit[i] == 0) {
//lostcost的值替换成邻接矩阵的值
//adjvex的值替换成邻接矩阵新顶点的值
lowcost[i] = matrix[flag][i];
adjvex[i] = a[flag];
}
}
}
}
//输出
cout << count << endl;
cout << "prim:" << endl;
for (int i = 0; i < k; i++) {
cout << a1[i] << " " << a2[i] << " " << a3[i] << endl;
}
}
Kruskal算法实现
void Kruskal() {
cout << "kruskal:" << endl;
//用parent数组来表示结点的双亲结点
//如结点4和结点1相连,则parent[4]=1
//parent数组都初始化为-1,当为-1时,表示是根节点
int* parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = -1;
}
//把相连的结点及其权值放入数组中,为后面排序做准备
string a1[100];//用来存放第1个结点
string a2[100];//用来存放第2个结点
int a3[100];//用来存放第1,2个结点间的权值
int k = 0;//数组下标
//只需要遍历邻接矩阵的上三角即可
for (int j = 0; j < n; j++) {
for (int i = 0; i < j; i++) {
if (matrix[i][j] != 0) {
//序号小的结点放在第1个结点存储数组(前面)
//序号大的结点放在第2个结点存储数组(后面)
if (find(a1[k]) > find(a2[k])) {
a1[k] = a[j];
a2[k] = a[i];
}
else {
a1[k] = a[i];
a2[k] = a[j];
}
a3[k] = matrix[i][j];
k++;
}
}
}
//排序:把结点间的权值按照从大到小的顺序排列
int temp1;
string temp2;
for (int i = 0; i < k - 1; i++) {
for (int j = i + 1; j < k; j++) {
if (a3[i] > a3[j]) {
//交换,小的放在数组前面
//注意:不仅仅交换的是权值,也要交换结点值
temp1 = a3[i];
a3[i] = a3[j];
a3[j] = temp1;
temp2 = a1[i];
a1[i] = a1[j];
a1[j] = temp2;
temp2 = a2[i];
a2[i] = a2[j];
a2[j] = temp2;
}
}
}
int num = 0;//当num=n-1时,循环跳出
for (int i = 0; i < k; i++) {
//分别找出两个比较结点的双亲是否相同(是否在同一个连通分量中)
string i1 = findRoot(parent, a1[i]);
string i2 = findRoot(parent, a2[i]);
//若不同,则相连(放入同一连通分量中)
if (i1 != i2) {
parent[find(i2)] = find(i1);//设置结点的双亲(连通分量)
cout << a1[i] << " " << a2[i] << " " << a3[i] << endl;
num++;
if (num == n - 1) {
break;
}
}
}
}
string findRoot(int parent[], string v) {
//这是一个判断结点的双亲的函数(根节点)
int t = find(v);
while (parent[t] > -1) {
t = parent[t];
}
return a[t];
}
完整代码
#include <iostream>
using namespace std;
class Map {
private:
int n;
string* a;
int** matrix;
int* visit;
public:
void set() {
//输入
cin >> this->n;
a = new string[n];
for (int i = 0; i < n; i++) {
cin >> a[i];
}
//初始化矩阵
matrix = new int* [n];
for (int i = 0; i < n; i++) {
matrix[i] = new int[n];
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
matrix[i][j] = 0;
}
}
//初始化visit数组
visit = new int[n];
for (int i = 0; i < n; i++) {
visit[i] = false;
}
//输入,构造邻接矩阵
int t;
cin >> t;
while (t--) {
string a;
string c;
int wei;
cin >> a >> c >> wei;
insert(a, c, wei);
}
string num;
cin >> num;
int i = find(num);
//Prim算法
Prim(i);
//Kruskal算法
Kruskal();
}
void insert(string a, string c, int wei) {
int finda = find(a);
int findc = find(c);
matrix[finda][findc] = wei;
matrix[findc][finda] = wei;
}
int find(string num) {
for (int i = 0; i < n; i++) {
if (a[i] == num) {
return i;
}
}
}
void Prim(int start) {
//数组shortEdge
string* adjvex = new string[n];//结点
int* lowcost = new int[n];//最小权值
//存储数组
string* a1 = new string[n];//存储第一个结点
string* a2 = new string[n];//存储第二个结点
int* a3 = new int[n];//存储第一个顶点和第二个顶点间的权值
int count = 0;//计数
int k = 0;//存储数组下标
int m = n - 1;
//初始化
for (int i = 0; i < n; i++) {
adjvex[i] = a[start];
lowcost[i] = matrix[start][i];
}
visit[start] = 1;//访问初始顶点
lowcost[start] = 0;//初始顶点的lowcost值赋0
//循环,不断更新存储值和shortEdge数组值
while (m--) {
int min = 99999;//最小权值数值标记
int flag = 0;//最小权值下标标记
//寻找最小权值
for (int i = 0; i < n; i++) {
//条件就是s2[i]不能等于0且小于min
if (lowcost[i] != 0 && lowcost[i] < min) {
min = lowcost[i];
flag = i;
}
}
//更新存储值
visit[flag] = 1;//visit设置成已访问结点
a1[k] = adjvex[flag];//第一个存储结点存储寻找到的下标对应的s1的值
a2[k] = a[flag];//第二个存储节点存储寻找到的下表对应的a的值(邻接矩阵结点)
a3[k] = min;//存储两者最小权值
count += min;
lowcost[flag] = 0;
k++;
//更新shortEdge数组
for (int i = 0; i < n; i++) {
//条件1:邻接矩阵的值小于lostcost数组的值 且 邻接矩阵的值不等于0
//条件2:lostcost数组的值等于0 且 邻接矩阵的值不等于0
//条件1,2的必要条件:visit数组的值等于0(还未被访问的顶点)
if ((matrix[flag][i] < lowcost[i] && matrix[flag][i] != 0) || (lowcost[i] == 0 && matrix[flag][i] != 0)) {
if (visit[i] == 0) {
//lostcost的值替换成邻接矩阵的值
//adjvex的值替换成邻接矩阵新顶点的值
lowcost[i] = matrix[flag][i];
adjvex[i] = a[flag];
}
}
}
}
//输出
cout << count << endl;
cout << "prim:" << endl;
for (int i = 0; i < k; i++) {
cout << a1[i] << " " << a2[i] << " " << a3[i] << endl;
}
}
void Kruskal() {
cout << "kruskal:" << endl;
//用parent数组来表示结点的双亲结点
//如结点4和结点1相连,则parent[4]=1
//parent数组都初始化为-1,当为-1时,表示是根节点
int* parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = -1;
}
//把相连的结点及其权值放入数组中,为后面排序做准备
string a1[100];//用来存放第1个结点
string a2[100];//用来存放第2个结点
int a3[100];//用来存放第1,2个结点间的权值
int k = 0;//数组下标
//只需要遍历邻接矩阵的上三角即可
for (int j = 0; j < n; j++) {
for (int i = 0; i < j; i++) {
if (matrix[i][j] != 0) {
//序号小的结点放在第1个结点存储数组(前面)
//序号大的结点放在第2个结点存储数组(后面)
if (find(a1[k]) > find(a2[k])) {
a1[k] = a[j];
a2[k] = a[i];
}
else {
a1[k] = a[i];
a2[k] = a[j];
}
a3[k] = matrix[i][j];
k++;
}
}
}
//排序:把结点间的权值按照从大到小的顺序排列
int temp1;
string temp2;
for (int i = 0; i < k - 1; i++) {
for (int j = i + 1; j < k; j++) {
if (a3[i] > a3[j]) {
//交换,小的放在数组前面
//注意:不仅仅交换的是权值,也要交换结点值
temp1 = a3[i];
a3[i] = a3[j];
a3[j] = temp1;
temp2 = a1[i];
a1[i] = a1[j];
a1[j] = temp2;
temp2 = a2[i];
a2[i] = a2[j];
a2[j] = temp2;
}
}
}
int num = 0;//当num=n-1时,循环跳出
for (int i = 0; i < k; i++) {
//分别找出两个比较结点的双亲是否相同(是否在同一个连通分量中)
string i1 = findRoot(parent, a1[i]);
string i2 = findRoot(parent, a2[i]);
//若不同,则相连(放入同一连通分量中)
if (i1 != i2) {
parent[find(i2)] = find(i1);//设置结点的双亲(连通分量)
cout << a1[i] << " " << a2[i] << " " << a3[i] << endl;
num++;
if (num == n - 1) {
break;
}
}
}
}
string findRoot(int parent[], string v) {
//这是一个判断结点的双亲的函数(根节点)
int t = find(v);
while (parent[t] > -1) {
t = parent[t];
}
return a[t];
}
};
int main()
{
Map map;
map.set();
return 0;
}