zoukankan      html  css  js  c++  java
  • 最短路径纯贪心算法。

     

        Dijkstra算法,书上其实说的很简洁,仔细看,仔细思考是会理解的.但要先理解几条引论和推理.

        而自己思考的思路在不需要任何推理只从贪心思路出发,和Dijkstra有所不同,但本质一样,那么自己按照理解,试着慢慢讲下.

    一,问题:

    从某个源点,到其他各点的最短路径.

    注意,不要想成某一个点,到某个点的最短路径.这样的话不太好推导出思路。

    某个源点,到其他各点的最短路径.这样的思路反而好推导出,反正所有点都计算出来了。某点到某点就是其中一条而已。,

    二,分析.

    先抛弃书中所有关于最短路径的引理,定理,公理....

    先看图.

      要找到从0点.到所有点的最短路径.

      假如,小明,站在0点.他很渴,天气很热,再不喝水要渴死,而每个其他点,都有一瓶水,点和点之间的交通工具还不一样,有的是汽车,有的还必须走路. 

      假设这里的权值代表两点之间所花费的时间,比如0 ~3中间的数字6, 表示.从0到3要6个小时的慢车.

      打开地图一看,到底哪个是最短啊.他想喝3点的水,发现要9个小时. 居然比 先到2,再到4, 7个小时还长,那么去4吧.你当小明傻啊.到了2点,不喝水,还去4点.

      所以小明得到一个常识,对于第一瓶水,需要中转点而到达的目的点肯定不是花费时间最少的,最起码可以喝那个中转站的水.

      直连的点话,看看哪个点花费的时间少.  1点最少,只要3个小时.

      废话少说,小明直接从0做车到1,花费3个小时,喝到了第一瓶水,刚喝完水,小明被时光机, 卟的一声,居然传回了0点.(因为我们要从0开始,找出到所有点的最短路径,所以小明被传送了.)

      因为小明还是很渴,天气依然很热,再不喝水要渴死.

      那么下一个最近的点在哪里?

      小明开始思索, 1点是和0直连最近的.已经被我喝掉了.那么舍去1点,第二近的直连点是不是最近的呢?

      先看看.发现了是6点.要4个小时.

      其他直连点先全部抛弃,因为这个时候6点是直连点最近的了.

      还有没有比4个小时更短的?有,是之前的1点,只要3个小时,但喝掉了.

      小明思索一会,有答案了,唯一可能的是,如果原来1点,离它很近的地方有顶点.那么有可能比6点近.

      看看地图,1可以到4和5.时间是5和8. 总时间就是 3+5和3+8.明显比4个小时长.

      小明马上又坐车从0点到了6点.喝光水,毫无疑问, 卟的一声小明被时光机,又传回了0点.

      不过有了上次的思考.

      小明有了初步总结.

      下一个最近的可能是2种方案中找一个

        1)和0直连的点,但要除去1,和6.也就是和0直连的 第3近的点.

        2)从最早的的1点出发,所能到达的点,虽然上次排除了,但说不定这次和第3近的点有的一拼.

      正要查地图的时候.小明突然想到. 6点是第二近的点.说不定从6点出发,也有比 直连第3近的点更短呢.

      终于,小明整理出了一个方案. 

      用一个数组MinRD[]  ,存放已知最短路径.

      用另外一个数组noGet[],存放 所有点 减去 所有最短路径的终点.

      每次从最短路径MinRD[]中,查询每条最短路径的终点. 再写下这些终点,到 未曾到达的点的权直.再找出最小的.

        这里要想明白, 假如有一条最短路径经过了好几个点,如 0->1->3->6->7. 小明最后一次喝掉了7点的水,

              也意味着,他之前喝掉了6点的水,也就是0->1->3->6是一个最短路径,    

              0->1->3->6->7是从0->1->3->6这个最短路径,由6顶点和其他最短路径的顶点通过残酷的比较中选出 来的.   

              这其实就是最优路径的的一个引论,最短路径,中间的路径也是最短路径.喝水喝出了引论.

      不厌其烦的,我要写下每次程序运行的过程.

    1)第一次找水.

    已知最短路径

    0                      这个是原点,默认放入.

    还未到达过的

    1,2,3,4,5,6

    只有一个终点0. 从终点出发,和还未到达过的点组成弧,找到最断的.

    比较 v(0,1),v(0,2),,v(0,3),v(0,4),v(0,5),v(0,6)

    0->1最短,加入到最短路径集合中.

    2)

    已知最短路径

    0

    0->1

    还未到达过的

    2,3,4,5,6

    第一个最短路径的终点是0.第二条最短路径的终点是1

    从终点0和1出发,和还未到达过的点组成弧,找到最断的.

    比较  v(0,2),,v(0,3),v(0,4),v(0,5),v(0,6),

            v(1,2),v(1,3),v(1,4),v(1,5),v(1,6)

    0->6 最短,加入到最短路径集合中

    3)

    已知最短路径

    0,                     

    0->1                第一次找到的.

    0->6                第二次找到的.

    还未到达过的

    2,3,4,5

    比较  v(0,2),,v(0,3),v(0,4),v(0,5)

            v(1,2),v(1,3),v(1,4),v(1,5)

       v(6,2),v(6,3),v(6,4),v(6,5)

    最优路径终点0,1,6,

    0->6->5 和 0->2 一样,

    根据程序判断顺序,会加入其中一个,下次查找会加入另外一个. 也可以修改程序,让他们一次加入

      c  代码如下(代码可读性一般)

    struct Minroads
    {
        int *road;
        int length;
        int cost;
    };
    
    
    int main()
    {
        //临街矩阵,直接写出,就不用程序生成了,数字表示2点间弧的权直,-1表示2点不能直达。
        int matrix[7][7]={
        0,3,5,9,-1,6,4,
        3,0,-1,-1,8,5,-1,
        5,-1,0,-1,2,-1,-1,
        9,-1,-1,0,-1,-1,-1,
        -1,8,2,-1,0,-1,-1,
        6,5,-1,-1,-1,0,1,
        4,-1,-1,-1,-1,1,0
        };
    
        struct Minroads MinRD[7];//初始化最短路径数组
        int i=0;
        for(i=0;i<7;i++)
        {
            MinRD[i].road=malloc(sizeof(int)*7);
            MinRD[i].road[0]=0;
            MinRD[i].cost=0;
            MinRD[i].length=0;
        }
    
        int noGet[7]={0,1,2,3,4,5,6};//初始化未曾到达的顶点集合。
    
    
        MinRD[0].road[0]=0;//把源点自己(起点)作为第一条最短路径加入最短路径集合(包含1个顶。权直和为0)
        MinRD[0].length=1;
        MinRD[0].cost=0;
    
        noGet[0]=-1;// 简单的把到达的点,标记为-1,0加入就把索引为0的数值改为-1。当nouse的元素全部为-1。那就全部到达。
        int get_count=1;//
    
    
    
        //从每条已知的最短路径出发,走一步看看。比较所有产生的新路径,看看谁最短。
        //程序描述就是找出 ,minrd中,每个road的权直,加上,这条road的顶点到noGet中所有顶点的权直中最小的。
        while(get_count<7)
        {
            int i_noget,i_shortest;
            int temp_short=0x7fffffff;//int 是有符号数,最高位为0,表示最大正数,
            int temp_s, tmep_d;
            for(i_shortest=0;i_shortest<7;i_shortest++)//所有最短路径
            {
                if(MinRD[i_shortest].length!=0)//
                {
                    for(i_noget=0;i_noget<7;i_noget++)//未曾到达的顶点
                    {
                        if(noGet[i_noget]!=-1)
                        {
                            int x;
                            x=MinRD[i_shortest].road[MinRD[i_shortest].length-1];//最短路径的顶点
                            int y=noGet[i_noget];
                            int newshort;
                            if(matrix[x][y]!=-1)
                            {
                                newshort=MinRD[i_shortest].cost+matrix[x][y];
                            }
                            else
                            {
                                newshort=0x7fffffff;
                            }
                            if(newshort<=temp_short)
                            {
                                temp_short=newshort;
                                temp_s=i_shortest;
                                tmep_d=i_noget;
                            }
                        }
                    }
                }
            }
    
    
            int i_copy;
            for(i_copy=0;i_copy<MinRD[temp_s].length;i_copy++)
            {
                MinRD[get_count].road[i_copy]=MinRD[temp_s].road[i_copy];
            }
            MinRD[get_count].road[i_copy]=tmep_d;
    
            MinRD[get_count].length=MinRD[temp_s].length+1;
            MinRD[get_count].cost+=temp_short;
    
            noGet[tmep_d]=-1;
    
            get_count++;
        }
    
        int i_print;
        for(i=0;i<7;i++)
        {
            for(i_print=0;i_print<MinRD[i].length;i_print++)
            {
                if(i_print!=MinRD[i].length-1)
                {
                    printf("%d->",MinRD[i].road[i_print]);
                }
                else
                {
                    printf("%d",MinRD[i].road[i_print]);
                }
    
            }
            printf("  cost:%d",MinRD[i].cost);
            printf("
    ");
        }
    }

    结果:

    再次注意这里

    0->6->5.这条最优路径找出来的话。上面必定有0->6这条最优路径。

    闭着眼睛想一下,0->6->5是最优的话,没有理由,0—>6不是。如果0->6不是,而是0->x.那么,0->x->5,肯定比0->6->5更短。

    Dijkstra(迪杰斯特拉),是每次加入一条最短路径,那么更新   每个未处理的点    到     最短路径的顶点集合  的最短路径直。 选出最小的那个直。

    注意当加入第一个原点的时候,本身的权直就相当于更新过一遍.

    因为更新过,

    能体现出动态调整的思路。

    而我们的思路

    体现的是贪心算法的思路。省掉了更新的步骤,每次重新计算所有的点.所以导致比Dijkstra运算量稍微大点。

    不过自己推导出来,并能理解的,总感觉更亲切。

    下篇看看怎么更容易理解的方式写出Dijkstra

    几年后翻回来看。其实自己的想出来的思路,完全正确。就是贪心思路。

    而Dijkstra是贪心+动态。当年用贪心想到就结束了。没有继续优化,保存中间值,否则就是Dijkstra算法了。

    夸奖一下自己很难吗?比心。

  • 相关阅读:
    监听器模式
    接口幂等性实现
    如何设计一个良好的API接口
    接口重试实现
    Spring不常用但有用的注解
    angular项目语言切换功能
    解决IOS上传竖向照片会旋转90度的问题
    微信点击链接:debugx5.qq.com提示您使用的不是x5内核
    swagger注释@API详细说明
    创建swap虚拟内存分区
  • 原文地址:https://www.cnblogs.com/lsfv/p/5532145.html
Copyright © 2011-2022 走看看