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



相信很多数据结构书上都讲了两种有关“最小生成树”的算法求解,两种都是基于贪心算法。其中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

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.如此类推...


#include 
#include 
using namespace std;

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

int map[N][N];    //二维数组表示图    从 (1顶点)开始 
int visited[N];		//表示各个定点是否已加入到了 1 中 
int low[N]   ;			//表示从  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 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<


对于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算法得到的最小生成树中v0v1是不直接相连的,也就是在最小生成树中v0v1的距离是v0->v2->v3->v1的距离是27,而用Dijkstra算法得到的v0v1的距离是20,也就是二者直接连线的长度。




你可能感兴趣的:(数据结构和算法)