数据结构与算法之PHP用邻接表、邻接矩阵实现图的深度优先遍历(DFS)

摘要:
1、 基本思想1)访问指定的起始顶点v;2) 从v的未访问的相邻点开始,首先遍历图的深度;直到访问图形中所有具有到v的路径的顶点;3) 如果此时图形中仍有尚未访问的顶点,请从尚未访问的一个顶点开始,重复深度优先遍历,直到访问完图形中的所有顶点。
一、基本思想
1)访问指定的起始顶点v;
2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。
 
二、图的存储结构
数据结构与算法之PHP用邻接表、邻接矩阵实现图的深度优先遍历(DFS)第1张数据结构与算法之PHP用邻接表、邻接矩阵实现图的深度优先遍历(DFS)第2张数据结构与算法之PHP用邻接表、邻接矩阵实现图的深度优先遍历(DFS)第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();

免责声明:文章转载自《数据结构与算法之PHP用邻接表、邻接矩阵实现图的深度优先遍历(DFS)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇开源的许可证GPL、LGPL、BSD、Apache 2.0的通俗解释[ELK] Elasticsearch 安装/配置、启动/停止、加节点/重启下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

LNMP环境的安装配置(简单版)

0、安装必要的依赖软件 如果已经安装了可能会进行升级,版本完全一致则不会进行任何操作。 yum -y install bzip2-devel curl-devel freetype-devel gcc libjpeg-devel libpng-devel libxslt-devel libxml2-devel openssl-devel pcre-deve...

php实现手机定位

mobile.PHP文件 当用户当手机访问该页面时,通过实现页面表单隐藏封装自动提交获取手机浏览器经纬度并post给服务器 <html> <meta charset="utf8"> <head> <form name="form1" action="http://test.nwee.cc/ken/baiduloc...

Apache2目录遍历漏洞修复

话说,我一直没注意到自己服务器上的apache有目录遍历漏洞,直到我学了web中间件漏洞 这个漏洞存在三个月了,当时是建靶场,在apache默认目录下新建了目录才出现的漏洞 漏洞截图: Ubuntu下apache默认安装目录,将所有<Directory>标签下的indexes删除,这样一来,如果找不到目标文件,就不会泄露目录,而是返回报错信息...

PHP 之sha256 sha512封装

PHP 之sha256 sha512封装/* PHP sha256 sha512目前(PHP 7.1)没有内置的函数来计算,sha1() sha1_file() md5() md5_file()分别可以用来计算字符串和文件的sha1散列值和md5散列值,当前最新版本PHP 7.1 sha256() sha256_file() sha512() sha512...

PHP7革新与性能优化

有幸参与2015年的PHP技术峰会(PHPCON),听了鸟哥(惠新宸)的关于PHP7的新特性和性能优化的分享,一切都令人感到激动。鸟哥是国内最权威的PHP专家,他的分享有很多非常有价值的东西,我通过整理分享的PPT和收集相关资料,整理为这篇解读性质的技术文章,希望能给做PHP开发的同学一些帮助。 PHP已经走过了20年的历史,直到今天,PHP7都发布了R...

[转]php cli命令 自定义参数传递

FROM : http://www.cnblogs.com/zcy_soft/archive/2011/12/10/2283437.html 所有的PHP发行版,不论是编译自源代码的版本还是预创建的版本,都在默认情况下带有一个PHP可执行文件。这个可执行文件可以被用来运行命令行的PHP程序。要在你的系统上找到这个可执行文件,就要遵照下面的步骤:      ...