Leetcode28

Dijkstra 算法 PHP 实现

单源最短路径算法,核心思想为把起点到所有点的距离存在一个数组中,然后广度优先遍历每次触及距离最短的点,然后更新接触到的点与起点的距离,下面是算法图示:

dijkstra

下面是的我的PHP版本实现

<?php
Class Vertex
{
    protected $id;
    protected $neighbors;
    protected $neighbor_weights;

    public function __construct($node)
    {
        $this->id = $node;
    }

    public function addNeighbor($vertex, int $weight)
    {
        $this->neighbors[] = $vertex;
        $this->neighbor_weights[$vertex] = $weight;
    }

    public function getNeighbors()
    {
        return $this->neighbors;
    }

    public function getId()
    {
        return $this->id;
    }

    public function getWeight($vertex)
    {
        return $this->neighbor_weights[$vertex];
    }
}

Class Graph
{
    protected $vertexes;
    protected $vertex_num;

    public function __construct()
    {
        $this->vertexes = [];
        $this->vertex_num = 0;
    }

    public function addVertex($node)
    {
        $vertex = new Vertex($node);
        $this->vertexes[$node] = $vertex;
        $this->vertex_num += 1;

        return $vertex;
    }

    public function getVertex($node)
    {
        return $this->vertexes[$node]?:NULL;
    }

    public function addEdge($from, $to, int $weight)
    {
        if (!array_key_exists($from, $this->vertexes)) {
            $this->addVertex($from);
        }

        if (!array_key_exists($to, $this->vertexes)) {
            $this->addVertex($to);
        }

        $this->vertexes[$from]->addNeighbor($to, $weight);
    }

    public function getVertexes()
    {
        return $this->vertexes;
    }

    public function findCirclesThroughVertex($vertex)
    {
        $circles = [];

        $this->dfs($vertex, $vertex, [], $circles);
        
        return $circles;
    }

    private function dfs($start, $vertex, $stack, &$circles)
    {
        $stack[] = $vertex;
        foreach ($this->getVertex($vertex)->getNeighbors() as $key => $value) {
            if (!in_array($value, $stack)) {
                $this->dfs($start, $value, $stack, $circles);
            } else {
                if ($start == $value) {
                    array_push($stack, $start);
                    $circles[] = $stack;
                }
            }
        }
    }


    public function findLinesPtp($start, $end, $deep)
    {
        $lineArr = [];
        $this->ptpDfs($start, $end, $sonDeep = 0, $deep, $stack = [], $lineArr);
        return $lineArr;
    }

    public function ptpDfs($start, $end, $sonDeep, $deep, $stack = [], &$lineArr)
    {
        var_dump($start . '->' . $end . ' deep:' . $sonDeep);
        $stack[] = $start;
        $sonDeep++;
        foreach ($this->getVertex($start)->getNeighbors() as $key => $value) {
            if ($sonDeep < $deep) {
                $this->ptpDfs($value, $end, $sonDeep, $deep, $stack, $lineArr);
            } elseif($value == $end && $sonDeep == $deep) {
                array_push($stack, $end);
                $lineArr[] = $stack;
            }
        }
    }

    /*
     * 最短路径 Dijkstra 算法(单源多路径算法)
     */
    public function findShortestPath($source, $destination)
    {
        // To do: 判断source 是否在集合里
        // 最短路径是否不存在
        $distances = [];
        $shortest_path = [$source];
        foreach ($this->getVertexes() as $id => $vertex) {
            $distances[$id] = $this->weightFromTwoVertex($source, $id);
        }

        $distances[$source] = 0;
        $S = [];
        $Q = array_keys($this->getVertexes());
        unset($Q[array_search($source, $Q)]);

        while ($Q) {
            $source = $this->ExtractMin($Q, $source);
            unset($Q[array_search($source, $Q)]);
            $S[] = $source;

            foreach ($this->getVertex($source)->getNeighbors() as $neighbor) {
                $multi_vertex_distance = $distances[$source] + $this->weightFromTwoVertex($source, $neighbor);
                if ($distances[$neighbor] > $multi_vertex_distance) {
                    $distances[$neighbor] = $multi_vertex_distance;
                    if ($neighbor == $destination) {
                        $shortest_path = array_merge($shortest_path, $S);
                    }
                }
            }
        }

        array_push($shortest_path, $destination);
        return $shortest_path;
    }

    private function weightFromTwoVertex($start, $end)
    {
        $neighbors = $this->getVertex($start)->getNeighbors();

        return in_array($end, $neighbors) ? $this->getVertex($start)->getWeight($end) : PHP_INT_MAX;
    }

    /**
     * 取出最近的点
     * @param [type] $Q         [description]
     * @param [type] $distances [description]
     */
    public function ExtractMin($Q, $source)
    {
        $min = PHP_INT_MAX;
        $vertex = $this->getVertex($source);
        $neighbors = $vertex->getNeighbors();
        $min_node = count($Q) == 1 ? array_pop($Q) : $source;
        foreach ($neighbors as $neighbor) {
            if ($vertex->getWeight($neighbor) < $min && in_array($neighbor, $Q)) {
                $min_node = $neighbor;
                $min = $vertex->getWeight($neighbor);
            }
        }

        // unset($Q[$min_node]);
        return $min_node;
    }
}


$graph = new Graph;
$graph->addEdge('A', 'B', 5);
$graph->addEdge('B', 'C', 4);
$graph->addEdge('C', 'D', 8);
$graph->addEdge('D', 'C', 8);
$graph->addEdge('D', 'E', 6);
$graph->addEdge('A', 'D', 5);
$graph->addEdge('C', 'E', 2);
$graph->addEdge('E', 'B', 3);
$graph->addEdge('A', 'E', 7);

// $neighbors = $graph->getVertex('A')->getNeighbors();
//$circles = $graph->findCirclesThroughVertex('C');

//var_dump($circles);

// A-C深度是4的路径
// $lines = $graph->atmostMaxLengthCircleCount($start = 'C', $end = 'C', $distance = 30);
// var_dump($lines);

// A-C最短路径
$shortest_path = $graph->findShortestPath('A', 'C');
var_dump($shortest_path);

折腾了一晚上,还是挺有成就感的,自己看wiki的简介然后实现,没有去看别人的代码。

这里有一个质量比较高的算法讲解资料,推荐给看到的人。

算法发明者

Edsger_Wybe_Dijkstra

Simpliety is prerequisite of reliability. —- Edsger Dijkstra


Recent posts

Leetcode30

ElasticSearch 系列(一)

Mysql 分区表实践

Kafka 入门

Hugo 安装


Related posts

Leetcode30

Leetcode29

Leetcode27

Leetcode26

Leetcode25

Leetcode24

Leetcode23


Archives

2020 (11)
2019 (56)