數據結構與算法之PHP用鄰接表、鄰接矩陣實現圖的深度優先遍歷(DFS)


一、基本思想
1)訪問指定的起始頂點v;
2)依次從v的未被訪問的鄰接點出發,對圖進行深度優先遍歷;直至圖中和v有路徑相通的頂點都被訪問;
3)若此時圖中尚有頂點未被訪問,則從一個未被訪問的頂點出發,重新進行深度優先遍歷,直到圖中所有頂點均被訪問過為止。
 
二、圖的存儲結構
                       示例圖                  圖的鄰接表存儲方式              圖的鄰接矩陣存儲方式
 
三、實現方式
1、鄰接表
<?php
/**
 * 圖的深度優先遍歷
 * 圖的存儲結構--鄰接表
 */
class Node{
    public $value = null;
    public $next = [];//存儲下一個節點位置的數組

    public function __construct($value = null){
        $this->value = $value;
    }
}

class Graph
{
    // 記錄節點是否已被遍歷
    public $visited = [];
    // 圖的鄰接表數組
    public $graph = [];

    /**
     * 為頂點添加鄰接點
     * @param $vertex 頂點v
     * @param $adjvex 頂點v的鄰接點
     */
    public function addVertex($vertex, $adjvex)
    {
        $this->graph[$vertex][] = $adjvex;
    }

    // 將鄰接表數組轉為鄰接鏈表
    public function createGraph()
    {
        $vertices = array_keys($this->graph);
        $result = [];
        foreach ($vertices as $vertex) {
            $result[$vertex] = new Node($vertex);
        }
        foreach ($this->graph as $vertex => $adjvex) {
            foreach ($adjvex as $v) {
                if (isset($result[$v]) && is_object($result[$v])) {
                    $result[$vertex]->next[] = $result[$v];
                }
            }
        }
        return $result;
    }

    /**
     * 遞歸
     * @param $v 傳入的是第一個需要訪問的頂點
     */
    public function dfs($v) {
        // 置已訪問標記
        $this->visited[$v] = 1;
        // 輸出被訪問頂點
        echo $v . PHP_EOL;
//        print_r($this->graph[$v]);die;
        for ($i = 0; $i < count($this->graph[$v]); $i++) {
            if ($this->visited[$this->graph[$v][$i]] == 0) {
                $this->dfs($this->graph[$v][$i]);
            } else {
                continue;
            }
        }
    }

    /**
     * 非遞歸
     * @param $v 傳入的是第一個需要訪問的頂點
     */
    public function deepFirstSearch($v) {
        // 初始化節點遍歷標記
        $vertices = array_keys($this->graph);
        foreach ($vertices as $vertex) {
            $this->visited[$vertex] = 0;
        }
        $stack[] = $v;
        while (!empty($stack)) {
            $current = array_pop($stack);
            if ($this->visited[$current->value] == 0) {
                echo $current->value . PHP_EOL;
                $this->visited[$current->value] = 1;
            }
            for ($i = count($current->next) - 1; $i >= 0; $i--) {
                if ($this->visited[$current->next[$i]->value] == 0) {
                    $stack[] = $current->next[$i];
                }
            }
        }
    }
}
// 測試
$vertices = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7', 'v8'];
$g = new Graph();
$g->addVertex('v1', 'v2');
$g->addVertex('v1', 'v3');
$g->addVertex('v2', 'v1');
$g->addVertex('v2', 'v4');
$g->addVertex('v2', 'v5');
$g->addVertex('v3', 'v1');
$g->addVertex('v3', 'v6');
$g->addVertex('v3', 'v7');
$g->addVertex('v4', 'v2');
$g->addVertex('v4', 'v8');
$g->addVertex('v5', 'v2');
$g->addVertex('v5', 'v8');
$g->addVertex('v6', 'v3');
$g->addVertex('v6', 'v7');
$g->addVertex('v7', 'v3');
$g->addVertex('v7', 'v6');
$g->addVertex('v8', 'v4');
$g->addVertex('v8', 'v5');
//print_r($g->graph);
// 遞歸
$g->dfs($vertices[0]);
// 非遞歸
$firstVertex = current($g->createGraph());
$g->deepFirstSearch($firstVertex);

2、鄰接矩陣

<?php
/**
 * 圖的深度優先遍歷
 * 圖的存儲結構--鄰接矩陣
 */
class Graph {
    // 存儲節點信息
    public $vertices;
    // 存儲邊信息
    public $arcs;
    // 圖的節點數
    public $vexnum;
    // 記錄節點是否已被遍歷
    public $visited = [];

    // 初始化
    public function __construct($vertices) {
        $this->vertices = $vertices;
        $this->vexnum = count($this->vertices);
        for ($i = 0; $i < $this->vexnum; $i++) {
            for ($j = 0; $j < $this->vexnum; $j++) {
                $this->arcs[$i][$j] = 0;
            }
        }
    }

    // 兩個頂點間添加邊(無向圖)
    public function addEdge($a, $b) {
        if ($a == $b) { // 邊的頭尾不能為同一節點
            return;
        }
        $this->arcs[$a][$b] = 1;
        $this->arcs[$b][$a] = 1;
    }

    // 從第i個節點開始深度優先遍歷
    public function traverse($i) {
        // 標記第i個節點已遍歷
        $this->visited[$i] = 1;
        // 打印當前遍歷的節點
        echo $this->vertices[$i] . PHP_EOL;
        // 遍歷鄰接矩陣中第i個節點的直接聯通關系
        for ($j = 0; $j < $this->vexnum ; $j++) {
            // 目標節點與當前節點直接聯通,並且該節點還沒有被訪問,遞歸
            if ($this->arcs[$i][$j] == 1 && $this->visited[$j] == 0) {
                $this->traverse($j);
            }
        }
    }

    // 遞歸
    public function dfs() {
        // 初始化節點遍歷標記
        for ($i = 0; $i < $this->vexnum; $i++) {
            $this->visited[$i] = 0;
        }
        // 從沒有被遍歷的節點開始深度遍歷
        for ($i = 0; $i < $this->vexnum; $i++) {
            if ($this->visited[$i] == 0) {
                // 若是連通圖,只會執行一次
                $this->traverse($i);
            }
        }
    }

    // 非遞歸
    public function deepFirstSearch() {
        // 初始化節點遍歷標記
        for ($i = 0; $i < $this->vexnum; $i++) {
            $this->visited[$i] = 0;
        }
        $stack = [];
        for ($i = 0; $i < $this->vexnum; $i++) {
            if (!$this->visited[$i]) {
                $stack[] = $i;
                while (!empty($stack)) {
                    // 出棧
                    $curr = array_pop($stack);
                    // 如果該節點還沒有被遍歷,則遍歷該節點並將子節點入棧
                    if ($this->visited[$curr] == 0) {
                        echo $this->vertices[$curr] . PHP_EOL;
                        $this->visited[$curr] = 1;
                        // 沒遍歷的子節點入棧
                        for ($j = $this->vexnum - 1; $j >= 0; $j--) {
                            if ($this->arcs[$curr][$j] == 1 && $this->visited[$j] == 0) {
                                $stack[] = $j;
                            }
                        }
                    }
                }
            }
        }
    }
}
// 測試
$vertices = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7', 'v8'];
$graph = new Graph($vertices);
$graph->addEdge(0, 1); // v1 v2
$graph->addEdge(0, 2); // v1 v3
$graph->addEdge(1, 3); // v2 v4
$graph->addEdge(1, 4); // v2 v5
$graph->addEdge(2, 5); // v3 v6
$graph->addEdge(2, 6); // v3 v7
$graph->addEdge(4, 7); // v5 v8
$graph->addEdge(3, 7); // v4 v8
// 遞歸
$graph->dfs();
// 非遞歸
$graph->deepFirstSearch();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM