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

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值