找最短路径算法(狄克斯特拉算法) php 实现

具体参考《算法图解》这本书第7章讲的,原书是用python写的,我用PHP再写一次,并稍加完善

把书上这三道练习题,拿来测试

找最短路径算法(狄克斯特拉算法) php 实现

 

网上再找了一个稍“难”点的题

找最短路径算法(狄克斯特拉算法) php 实现

 

上代码:


class ShortPath{
    protected $graph=[];//只需要存每个节点所对应的邻居们,就能表示一个图了

    protected $parents=[];//保存父级关系,最终能通过这个数组,回溯走过的最短路线

    protected $costs;//从起点,到每个节点的花销

    protected $infinity=999999;//用一个很大的数字,来代表一个正无穷大

    protected $processed=[];//已处理过的节点的key


    public function __construct($graph)
    {
        $this->graph=$graph;

        //初始化父级数组, 即:只知道start的邻居,就是start的父级
        foreach ($this->graph['start'] as $k=>$v) {
            $this->parents[$k]='start';
        }

        //初始化花销数组,即:只知道start的邻居节点的花销,其它节点都是正无穷
        foreach ($this->graph as $k=>$v) {
            if($k=='start'){
                $this->costs=$this->graph['start'];
            }elseif(empty($this->costs[$k])){
                $this->costs[$k]=$this->infinity;
            }
        }
        $this->costs['finish']=$this->infinity;
    }

    public function find(){
        $nodeKey=$this->findLowestCostNode($this->costs);
        while ($nodeKey){//只要还有未处理过的节点
            $cost=$this->costs[$nodeKey];
            $neighbors=$this->graph[$nodeKey] ?? [];//取邻居
            //遍历邻居
            foreach ($neighbors as $k=> $v) {
                $newCost=$cost+$v;
                if($this->costs[$k] > $newCost){
                    $this->costs[$k]=$newCost;//将更低的开销更新
                    $this->parents[$k]=$nodeKey;
                }
            }

            //调试输出
            echo '当前节点:' .$nodeKey . PHP_EOL;
            echo '邻居们:' . PHP_EOL;
            print_r($neighbors);
            echo '花销:' . PHP_EOL;
            print_r($this->costs);

            //既然已处理完了该节点的所有邻居,那么将该节点标记为已处理过了
            $this->processed[]=$nodeKey;
            //找出下一个待处理的节点
            $nodeKey=$this->findLowestCostNode();
        }
        $this->showResult();
    }

    //在未处理过的节点中,查找出最短的那个
    protected function findLowestCostNode(){
        $lowestCost=$this->infinity;//在这里设置为正无穷是为了,在第一次进入循环时,必然比第一个元素大
        $lowestCostKey=false;//要返回的最短路径节点的key
        foreach ($this->costs as $k=>$v) {
            if($v<$lowestCost && !in_array($k,$this->processed)){
                $lowestCost=$v;
                $lowestCostKey=$k;
            }
        }
        return $lowestCostKey;
    }

    //美化显示处理结果
    protected function showResult(){
        $path=[];
        $key='finish';
        //从终点开始,查找父级,回溯出最短路线
        while (isset($this->parents[$key])){
            array_unshift($path,$key);
            $key=$this->parents[$key];
        }
        echo '最短路径全长是:' . $this->costs['finish'] . PHP_EOL;
        echo '路线是:start->' . implode('->',$path) . PHP_EOL;
    }

}

//对应练习题A
$graph1=[
    "start"=>[
        "a"=>2,
        "b"=>5,
    ],
    "a"=>[
        "b"=>8,
        "d"=>7,
    ],
    "b"=>[
        "c"=>4,
        "d"=>2,
    ],
    "c"=>[
        "finish"=>3,
        "d"=>6
    ],
    "d"=>[
        "finish"=>1
    ]
];

//对应练习题B
$graph2=[
    "start"=>[
        "a"=>10,
    ],
    "a"=>[
        "c"=>20,
    ],
    "b"=>[
        "a"=>1,
    ],
    "c"=>[
        "b"=>1,
        "finish"=>30,
    ],
];


//对应练习题C。这里能看出,该算法不适用于有负权边的情况
$graph3=[
    "start"=>[
        "a"=>2,
        "b"=>2,
    ],
    "a"=>[
        "c"=>2,
        "finish"=>2
    ],
    "b"=>[
        "a"=>2,
    ],
    "c"=>[
        "b"=>-100,
        "finish"=>2,
    ],
];

//对应最后一题,将图中的v0,改为start,v8改为finish
$graph4=[
    "start"=>[
        "v1"=>1,
        "v2"=>5,
    ],
    "v1"=>[
        "start"=>1,
        "v2"=>3,
        "v3"=>7,
        "v4"=>5,
    ],
    "v2"=>[
        "start"=>5,
        "v1"=>3,
        "v4"=>1,
    ],
    "v3"=>[
        "v1"=>7,
        "v2"=>2,
        "v6"=>3,
    ],
    "v4"=>[
        "v1"=>5,
        "v2"=>1,
        "v3"=>2,
        "v5"=>3,
        "v6"=>6,
        "v7"=>9,
    ],
    "v5"=>[
        "v2"=>6,
        "v4"=>3,
        "v7"=>5,
    ],
    "v6"=>[
        "v3"=>3,
        "v4"=>6,
        "v7"=>2,
        "finish"=>7
    ],
    "v7"=>[
        "v4"=>9,
        "v5"=>5,
        "v6"=>2,
        "finish"=>4
    ],
];

$obj=new ShortPath($graph4);
$obj->find();

最后一题的结果输出展示 

找最短路径算法(狄克斯特拉算法) php 实现

 

最后附上《算法图解》这本书的pdf版

链接:https://pan.baidu.com/s/1Nditk7Waynn7c9FZ3ldnGw  密码:ij0r

找最短路径算法(狄克斯特拉算法) php 实现找最短路径算法(狄克斯特拉算法) php 实现 60岁老猿 发布了18 篇原创文章 · 获赞 15 · 访问量 4万+ 私信 关注
上一篇:Debug小锦囊


下一篇:ComboBox Control Messages 消息