您的位置:首页 > 其它

贪心算法 最小生成树prim与单源最短路径dijkstra

2016-06-15 15:30 585 查看
转自:http://blog.csdn.net/kejie1235/article/details/8145676



相信很多数据结构书上都讲了两种有关“最小生成树”的算法求解,两种都是基于贪心算法。其中prim算法是适合于稠密图,他的基本思想是从一个节点开始,沿着它的边寻找最短路径的边,并将其并入到开始节点中去,然后再次从开始节点以及刚刚并入的节点的边中寻找”最短路径“并入其中,然后依次循环。。。。
其中的原理很简单,很多数据结构的书上都讲过,但是其中的代码对于初学者还是比较困难的(比如我),我再网上找了很多资料然后发现其中一种思想比较简单,再这里跟大家分享。

 首先,要用二维数组记录点和权值。如上图所示无向图:

int map[7][7];

       map[1][2]=map[2][1]=4;

       map[1][3]=map[3][1]=2;

       ......

      然后再求最小生成树。具体方法是:

1.先选取一个点作起始点,然后选择它邻近的权值最小的点(如果有多个与其相连的相同最小权值的点,随便选取一个)。如1作为起点。

visited[1]=1;

pos=1;

//用low[]数组不断刷新最小权值,low[i](0<i<=点数)的值为:i点到邻近点(未被标记)的最小距离。

low[1]=0;  //起始点i到邻近点的最小距离为0

low[2]=map[pos][2]=4;

low[3]=map[pos][3]=2;

low[4]==map[pos][4]=3;

low[5]=map[pos][5]=MaxInt;  //无法直达

low[6]=map[pos][6]=MaxInt;

 

  2.再在伸延的点找与它邻近的两者权值最小的点。

//low[]以3作当前位置进行更新

visited[3]=1;

pos=3;

low[1]=0;   //已标记,不更新

low[2]=map[1][2]=4;  //比5小,不更新

low[3]=2;  //已标记,不更新

low[4]=map[1][4]=3;   //比1大,更新后为:low[4]=map[3][4]=1;

low[5]=map[1][5]=MaxInt;//无法直达,不更新

low[6]=map[1][6]=MaxInt;//比2大,更新后为:low[6]=map[3][6]=2;

 

    3.如此类推...



[cpp] view plain copy

#include <iostream>
#include <string.h>
using namespace std;

const int MAX=0x3f3f3f3f;
const int N =100;

int map

;    //二维数组表示图    从 (1顶点)开始
int visited
;     //表示各个定点是否已加入到了 1 中
int low
;          //表示从  1  从到各个定点的最小路径

int prim(int n)
{

memset(visited,0,sizeof(visited) );
memset(low,MAX,sizeof(low) );
int i,j,result = 0;     //表示result各个最小权值之和
int pos = 1;         //表示当前加入 1 的顶点
visited[1] = 1;
int min;   //表示新加入的顶点与 1的比较

for( i=1; i<=n; i++)
if( i != pos )
low[i] = map[pos][i] ;

//进行 n-1次循环,讲其中的 n-1个顶点并入 1中
for(i=1; i<n; i++ )
{
min = MAX;
for(j=1;j<=n;j++)
if( visited[j]==0 && low[j]< min )
{
min = low[j];
pos = j;
}

result+=min;        //计算最短路径总和
visited[pos] = 1;   //将最短的顶点加入到 1 中

//对于新加进来的顶点,将 map[pos][i]与low[i]进行比较,然后更新 low,low【】是专门对 1 号顶点设置的数组
//更新low数组
for(j=1; j<=n;j++)
{
if( visited[j]==0 && low[j] > map[pos][j])
low[j] = map[pos][j];
}
//下面是dijkstra与prim算法的区别 贪心时两者的贪心方式不同
/*for(j=1; j<=n; j++)
{
if(visited[j]== 0; map[pos][j]+low[pos] < low[j] )
low[j] = map[pos][j]+low[pos];
} */
}
return result;

}

int main()
{
int n;              //顶点个数
while( scanf("%d", &n)!=EOF )
{
int v;
memset(map,MAX,sizeof(map) );
for(int i=1; i<=n ; i++)
for(int j=1; j<=n; j++)
{
cin>>v;
if( v )             //对于map[i][i]以及不可到达  我们输入0
map[i][j] = v;
}

/*for(int i =1; i<=n; i++)        //用来测试输入是否正确,
{
for(int j=1; j<=n; j++)
cout<<map[i][j]<<" ";

cout<<endl;
}*/
cout<<"The smallest load :"<<prim(n)<<endl;
}
return 1;
}


对于prim程序的实现的思想我们来总结下: 1:其中low数组是对于 1的专用数组,其中low[i]是表示 1 到 i 的最短路径,我们首先从 1出发,找到最短路径 3 ,然后我们将3 并入1 中,也就是将visited[3] = 1; result+=min(其中min是 1到3的路径),然后我们再更新low[],以为3并入1后,我们需要找到1和3两者到其他路径的最短长度,然后将其中的较短者
赋给low。
2:我们再从low中寻找最小路径 并入1 中,然后再更新新节点到1中。一次循环n-1次。。。。

我们始终比较的是low数组,其中的最小值,得出最小值后,然后将新节点与low比较,更新low,始终保持low是当前路径的最小值,然后再次对low进行比较,个人以为这种编程思想适合新手理解,所以在这里和大家分享。

对于dijkstra而言,两者有些细微的差别:

二者的不同之处在于“权值最低”的定义不同,Prim的“权值最低”是相对于U中的任意一点而言的,也就是把U中的点看成一个整体,每次寻找V-U中跟U的距离最小(也就是跟U中任意一点的距离最小)的一点加入U;而Dijkstra的“权值最低”是相对于v0而言的,也就是每次寻找V-U中跟v0的距离最小的一点加入U。

一个可以说明二者不等价的例子是有四个顶点(v0, v1, v2, v3)和四条边且边值定义为(v0,
v1)=20, (v0, v2)=10, (v1, v3)=2, (v3, v2)=15的图,用Prim算法得到的最小生成树中v0跟v1是不直接相连的,也就是在最小生成树中v0v1的距离是v0->v2->v3->v1的距离是27,而用Dijkstra算法得到的v0v1的距离是20,也就是二者直接连线的长度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: