算法简介
普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。
图解
1、原始的加权连通图。每条边一侧的数字代表其权值。
2、顶点 D D D被任意选为起始点。顶点 A 、 B 、 E A、B、E A、B、E和 F F F通过单条边与 D D D相连。 A A A是距离 D D D最近的顶点,因此将 A A A 及对应边 A D AD AD以高亮表示。
3、下一个顶点为距离 D 或 A D或A D或A最近的顶点。 B 距 D 为 9 , 距 A 为 7 , E 为 15 , F 为 6 B距D为9,距A为7,E为15,F为6 B距D为9,距A为7,E为15,F为6。因此, F 距 D 或 A F距D或A F距D或A最近,因此将顶点 F 与 相 应 边 D F F与相应边DF F与相应边DF以高亮表示。
4、 算 法 继 续 重 复 上 面 的 步 骤 。 距 离 A 为 7 的 顶 点 B 被 高 亮 表 示 算法继续重复上面的步骤。距离A为7的顶点B被高亮表示 算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。
5、在当前情况下,可以在 C 、 E 与 G C、E与G C、E与G间进行选择。 C 距 B 为 8 , E 距 B 为 7 , G 距 F 为 11 C距B为8,E距B为7,G距F为11 C距B为8,E距B为7,G距F为11。点 E E E最近,因此将顶点 E E E与相应边 B E BE BE高亮表示。
6、这里,可供选择的顶点只有 C 和 G 。 C 距 E 为 5 , G 距 E 为 9 , 故 选 取 C , 并 与 边 E C C和G。C距E为5,G距E为9,故选取C,并与边EC C和G。C距E为5,G距E为9,故选取C,并与边EC一同高亮表示。
7、顶点 G G G是唯一剩下的顶点,它距 F F F为 11 11 11, 距 E 为 9 , E 最 近 , 故 高 亮 表 示 G 及 相 应 边 E G 距E为9,E最近,故高亮表示G及相应边EG 距E为9,E最近,故高亮表示G及相应边EG。
8、所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为 39 39 39。
代码分析
把图存入 G r a p h [ N ] [ N ] Graph[N][N] Graph[N][N]
int Graph[N][N]={
{
M, M, M, M, M, M, M},
{
M, 0, 6, 1, 5, M, M},
{
M, 6, 0, 5, 3, M, M},
{
M, 1, 5, 0, 5, 6, 4},
{
M, 5, 3, 5, 0, M, 2},
{
M, M, M, 6, M, 0, 6},
{
M, M, M, 4, 2, 6, 0},
};
调用 P r i m Prim Prim方法
int prim(int graph[][N])
{
int n = 6;
int lowcost[M];//记录边对应的权值
int mst[M];//记录权值对应的边 起点是值 终点是下标
int i, j, min, minid, sum = 0;
for (i = 2; i <= n; i++)
{
lowcost[i] = graph[1][i];
mst[i] = 1;
}
mst[1] = 0;
for (i = 2; i <= n; i++)
{
min = M;
minid = 0;
for (j = 2; j <= n; j++)
{
if (lowcost[j] < min && lowcost[j] != 0)
{
min = lowcost[j];
minid = j;
}
}
cout << "V" << mst[minid] << "-V" << minid << "=" << min << endl;
sum += min;
lowcost[minid] = 0;
for (j = 2; j <= n; j++)
{
if (graph[minid][j] < lowcost[j])
{
lowcost[j] = graph[minid][j];
mst[j] = minid;
}
}
}
return sum;
}
P r i m Prim Prim方法中第一个 f o r for for循环把第一个点对应的边(权值)放入 l o w c o s t [ i ] lowcost[i] lowcost[i]数组中
边 m s t [ ] mst[] mst[]数组,其下标记录终点,下标对应的值是起点。这样就记录了一条边
主要做一个初始化的工作
int lowcost[M];//记录边对应的权值
int mst[M];//记录权值对应的边 起点是值 终点是下标
int i, j, min, minid, sum = 0;
for (i = 2; i <= n; i++)
{
lowcost[i] = graph[1][i];
mst[i] = 1;
}
mst[1] = 0;
第二个 f o r for for循环,每一次都能找到一条最小的边
for (i = 2; i <= n; i++)
{
min = M;
minid = 0;
for (j = 2; j <= n; j++)
{
if (lowcost[j] < min && lowcost[j] != 0)
{
min = lowcost[j];
minid = j;
}
}
cout << "V" << mst[minid] << "-V" << minid << "=" << min << endl;
sum += min;
lowcost[minid] = 0;
for (j = 2; j <= n; j++)
{
if (graph[minid][j] < lowcost[j])
{
lowcost[j] = graph[minid][j];
mst[j] = minid;
}
}
}
第二个 f o r for for循环中的第一个 f o r for for循环会找到当前边集中权值最小的,并且记录其权值和下标.
min = M;
minid = 0;
for (j = 2; j <= n; j++)
{
if (lowcost[j] < min && lowcost[j] != 0)
{
min = lowcost[j];
minid = j;
}
}
cout << "V" << mst[minid] << "-V" << minid << "=" << min << endl;
sum += min;
lowcost[minid] = 0;
第二个 f o r for for循环中的第二个 f o r for for循环会找到当前新加入的边中权值比已经存入的更小的,并且记录其权值和下标.
for (j = 2; j <= n; j++)
{
if (graph[minid][j] < lowcost[j])
{
lowcost[j] = graph[minid][j];
mst[j] = minid;
}
}
代码
#include<iostream>
using namespace std;
#define M 65535 //无穷大
#define N 8 //顶点数
int prim(int graph[][N])
{
int n = 6;
int lowcost[M];//记录边对应的权值
int mst[M];//记录权值对应的边 起点是值 终点是下标
int i, j, min, minid, sum = 0;
for (i = 2; i <= n; i++)
{
lowcost[i] = graph[1][i];
mst[i] = 1;
}
mst[1] = 0;
for (i = 2; i <= n; i++)
{
min = M;
minid = 0;
for (j = 2; j <= n; j++)
{
if (lowcost[j] < min && lowcost[j] != 0)
{
min = lowcost[j];
minid = j;
}
}
cout << "V" << mst[minid] << "-V" << minid << "=" << min << endl;
sum += min;
lowcost[minid] = 0;
for (j = 2; j <= n; j++)
{
if (graph[minid][j] < lowcost[j])
{
lowcost[j] = graph[minid][j];
mst[j] = minid;
}
}
}
return sum;
}
int main()
{
int cost;
int Graph[N][N]={
{
M, M, M, M, M, M, M},
{
M, 0, 6, 1, 5, M, M},
{
M, 6, 0, 5, 3, M, M},
{
M, 1, 5, 0, 5, 6, 4},
{
M, 5, 3, 5, 0, M, 2},
{
M, M, M, 6, M, 0, 6},
{
M, M, M, 4, 2, 6, 0},
};
//求解最小生成树
cost = prim(Graph);
//输出最小权值和
cout << "最小权值和=" << cost << endl;
system("pause");
return 0;
}