一、基本思想
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();
