prim算法是求图的最小生成树的一种算法,它是根据图中的节点来进行求解,具体思想大概如下:
首先,将图的所有节点(我们假定总共有n个节点)分成两个集合,V和U。其中,集合V保存的是我们已经访问过的节点,集合U保存的是我们未曾访问的节点。prim算法第一步就是选定第一个节点放入集合V中(一般是第一个),然后查找V中节点和U中节点之间距离最小的那个路径,(假设U中节点w离V中的某个节点距离最近),将w放入V中,将w从U中删除,这样,直到U中所有节点都被放入V中,那么我们就找到了最小生成树。
因为prim算法每一步都需要遍历集合V和集合U中的所有节点,其时间复杂度为O(n*n),这是未经任何优化的prim算法时间复杂度。
一般,我们要对该算法进行优化,我们维护一个数组low[n],用来保存到每个节点的已知的最短路径是多少,例如,我们有图如下:
首先,我们选定v1作为我们的初始节点,这时,low数组的大小分别为:
low[1] = 0;
low[2] = 6;
low[3] = 1;
low[4] = 5;
low[5] = INF;
low[6] = INF;
因为5和6从1无法到达,所以设定为无穷大。
此时,集合V中有节点{V1},集合U中有节点{V2,V3,V4, V5,V6},此时,通过条件判断,我们可以找到节点3离V1最近(通过low数组),然后,我们将节点V3放入集合V中,并从U中删除它,此时
集合V= {V1,V3}
集合U={V2,V4,V5,V6}
这时,我们需要对low数组进行更新。前面讲过,low数组是保存的我们已知的点到剩余节点的最短距离,因为此时V3加入了已知点集,所以需要根据V3到其它节点的距离进行更新;
low[1] = 0;//因为节点V1已经被访问过,所以不需要更新
low[2] = 5;//因为从节点V3到节点V2的距离为5,比上一个数据low[2] = 6要小,因此需要更新;
low[3] = 1;//因为节点V3已经被访问过,所以不需要更新;
low[4] = 5;//因为从节点V3到V4的距离和上一个数据low[4]=5一样,我们不更新;
low[5] = 6;//很明显,此时V3到V5的距离要比上一个数据low[5]=INF来的小,我们进行更新;
low[6] = 4;//道理同low[5];
此时,我们对low数组进行更新完毕,这样,下次就可以继续通过low数组来查找最距离最短的那个点。
prim核心代码如下:
#include <iostream>
using namespace std;
void prim()
{
const int INF = 65536;//表示无穷大
const int N = 100;//N为图中所有的节点数
bool visited[N+1] = {false};//数组visited保存节点是否有被访问过,初始化为false
int low[N+1];//保存已访问节点离未访问节点的最短距离
int dist[N+1][N+1];//距离矩阵,用来保存图中节点之间的距离
int result = 0;//最短距离
int edge[N+1];//用来保存选择的点的依附节点
int min = INF;
int v;
//////////////////初始化矩阵/////////////////////////
for (int i=1; i<=N; ++i)
{
for (int j=1; j<=N; ++j)
{
if (i == j)
{
dist[i][j] = 0;
}
else
dist[i][j] = INF;
}
}
//////////////////输入边的信息//////////////////////
int m, a, b, c;
cout << "输入总的边数:";
cin >> m;
cout << "输入边的两个节点和边的长短:" << endl;
for (int i=1; i<=m; ++i)
{
cin >> a >> b >> c;
dist[a][b] = dist[b][a] = c;
}
//////////////////////////////////////////////////
visited[1] = true;//我们选取第一个节点为初始节点
low[1] = 0;
edge[1] = 1;
//初始化low和edge
for (int i=2; i<=N; ++i)
{
low[i] = dist[1][i];
edge[i] = 1;//初始化所有的节点依附节点为节点1
}
//循环N-1次,来求得最小生成树所花费的最短路径,因为有N个点,所以树有N-1个边
for (int i=1; i<N; ++i)
{
min = INF;
for (int j=1; j<=N; ++j)
{
if (!visited[j] && low[j]<min)//如果节点j没有被访问过并且当前路径小于min
{
min = low[j];
v = j;
}
}
//找到未被访问的节点V路径最短
result += min;
visited[v] = true;
cout << "(" << v << "," << edge[v] << ")" << endl;//输出找到的这个点所依附的节点,相当于输出一条边
//更新数组low和edge
for (int i=1; i<=N; ++i)
{
if (!visited[i] && low[i]>dist[v][i])//如果节点i没有被访问过并且当前路径小于已知的路径low
{
low[i] = dist[v][i];
edge[i] = v;//将节点i的新依附节点设置为找到的节点v
}
}
}
cout << result << endl;
return ;//完毕后,得到的result即为最小生成树最短路径长度,数组edge即为选择的节点边的信息
}
int main()
{
prim();
system("pause");
return 0;
}
若有错误,欢迎指出。另外,我们假定节点编号从1开始,而不是从0开始。