当前位置: 首页 > news >正文

深度优先遍历 和 广度优先遍历

————— 第二天 —————

什么是 深度/广度 优先遍历?

深度优先遍历简称DFS(Depth First Search),广度优先遍历简称BFS(Breadth First Search),它们是遍历图当中所有顶点的两种方式。

这两种遍历方式有什么不同呢?我们来举个栗子:

我们来到一个游乐场,游乐场里有11个景点。我们从景点0开始,要玩遍游乐场的所有景点,可以有什么样的游玩次序呢?

第一种是一头扎到底的玩法。我们选择一条支路,尽可能不断地深入,如果遇到死路就往回退,回退过程中如果遇到没探索过的支路,就进入该支路继续深入。

在图中,我们首先选择景点1的这条路,继续深入到景点7、景点8,终于发现走不动了(景点旁边的数字代表探索次序):

于是,我们退回到景点7,然后探索景点10,又走到了死胡同。于是,退回到景点1,探索景点9:

按照这个思路,我们再退回到景点0,后续依次探索景点2、3、5、4、6,终于玩遍了整个游乐场:

像这样先深入探索,走到头再回退寻找其他出路的遍历方式, 就叫做深度优先遍历(DFS)。

除了像深度优先遍历这样一头扎到底的玩法以外,我们还有另一种玩法:首先把起点相邻的几个景点玩遍,然后去玩距离起点稍远一些(隔一层)的景点,然后再去玩距离起点更远一些(隔两层)的景点......

在图中,我们首先探索景点0的相邻景点1、2、3、4:

接着,我们探索与景点0相隔一层的景点7、9、5、6:

最后,我们探索与景点0相隔两层的景点8、10:

像这样一层一层由内而外的遍历方式, 就叫做广度优先遍历(BFS)。

深度/广度优先遍历 的实现

深度优先遍历

首先说说深度优先遍历的实现过程。这里所说的回溯是什么意思呢?回溯顾名思义,就是自后向前,追溯曾经走过的路径。

我们把刚才游乐场的例子抽象成数据结构的图,假如我们依次访问了顶点0、1、7、8,发现无路可走了,这时候我们要从顶点8退回到顶点7。

而后我们探索了顶点10,又无路可走了,这时候我们要从顶点10退回到顶点7,再退回到顶点1。

像这样的自后向前追溯曾经访问过的路径,就叫做回溯。

要想实现回溯,可以利用栈的先入后出特性,也可以采用递归的方式(因为递归本身就是基于方法调用栈来实现)。

下面我们来演示一下具体实现过程。

首先访问顶点0、1、7、8,这四个顶点依次入栈,此时顶点8是栈顶:

从顶点8退回到顶点7,顶点8出栈:
接下来访问顶点10,顶点10入栈:

从顶点10退到顶点7,从顶点7退到顶点1,顶点10和顶点7出栈:

探索顶点9,顶点9入栈:

以此类推,利用这样一个临时栈来实现回溯,最终遍历完所有顶点。

广度优先遍历

接下来该说说广度优先遍历的实现过程了。刚才所说的重放是什么意思呢?似乎听起来和回溯差不多?其实,回溯与重放是完全相反的过程。

仍然以刚才的图为例,按照广度优先遍历的思想,我们首先遍历顶点0,然后遍历了邻近顶点1、2、3、4:

接下来我们要遍历更外围的顶点,可是如何找到这些更外围的顶点呢?我们需要把刚才遍历过的顶点1、2、3、4按顺序重新回顾一遍,从顶点1发现邻近的顶点7、9;从顶点3发现邻近的顶点5、6。

像这样把遍历过的顶点按照之前的遍历顺序重新回顾,就叫做重放。同样的,要实现重放也需要额外的存储空间,可以利用队列的先入先出特性来实现。

下面我们来演示一下具体实现过程。

首先遍历起点顶点0,顶点0入队:

接下来顶点0出队,遍历顶点0的邻近顶点1、2、3、4,并且把它们入队:

然后顶点1出队,遍历顶点1的邻近顶点7、9,并且把它们入队:

然后顶点2出队,没有新的顶点可入队:

以此类推,利用这样一个队列来实现重放,最终遍历完所有顶点。


/**
 * 图的顶点
 */
private static class Vertex {
    int data;
    Vertex(int data) {
        this.data = data;
    }
}

/**
 * 图(邻接表形式)
 */
private static class Graph  {
private int size;
private Vertex[] vertexes;
private LinkedList<Integer> adj[];
 Graph(int size){
this.size = size;
//初始化顶点和邻接矩阵
vertexes = new Vertex[size];
adj = new LinkedList[size];
for(int i=0; i<size; i++){
vertexes[i] = new Vertex(i);
adj[i] = new LinkedList();
        }
    }
}

/**
 * 深度优先遍历
 */
public static void dfs(Graph graph, int start, boolean[] visited) {
    System.out.println(graph.vertexes[start].data);
    visited[start] = true;
    for(int index : graph.adj[start]){
        if(!visited[index]){
            dfs(graph, index, visited);
        }
    }
}

/**
 * 广度优先遍历
 */
public static void bfs(Graph graph, int start, boolean[] visited, LinkedList<Integer> queue) {
    queue.offer(start);
    while (!queue.isEmpty()){
        int front = queue.poll();
        if(visited[front]){
            continue;
        }
        System.out.println(graph.vertexes[front].data);
        visited[front] = true;
        for(int index : graph.adj[front]){
            queue.offer(index);;
        }
    }
}


public static void main(String[] args) {
    Graph graph = new Graph(6);

    graph.adj[0].add(1);
    graph.adj[0].add(2);
    graph.adj[0].add(3);

    graph.adj[1].add(0);
    graph.adj[1].add(3);
    graph.adj[1].add(4);

    graph.adj[2].add(0);

    graph.adj[3].add(0);
    graph.adj[3].add(1);
    graph.adj[3].add(4);
    graph.adj[3].add(5);

    graph.adj[4].add(1);
    graph.adj[4].add(3);
    graph.adj[4].add(5);

    graph.adj[5].add(3);
    graph.adj[5].add(4);

    System.out.println("图的深度优先遍历:");
    dfs(graph, 0, new boolean[graph.size]);
    System.out.println("图的广度优先遍历:");
    bfs(graph, 0, new boolean[graph.size], new LinkedList<Integer>());
}
复制代码

note:生命太短暂,不要去做一些根本没有人想要的东西。

转载于:https://juejin.im/post/5c9a468c51882531f12dcd7c

相关文章:

  • 如何利用 Webshell 诊断 EDAS Serverless 应用
  • web接口中BigDecimal值比较不相等
  • Cable:360实现的新虚拟网络架构
  • ubuntu添加普通用户,并解决远程登录
  • 扫描自定义注解并在spring容器中注入自定义bean
  • Mac osx 系统安装 eclipse
  • 项目实战8.2-Linux下Tomcat开启查看GC信息
  • CopyTranslator v0.0.8 Zouwu RC1 发布
  • Mars 1.3.0 发布,微信官方跨平台跨业务终端基础组件
  • 华为6.0系统(亲测有效)激活XPOSED框架的方法
  • SOP 1.1.0 发布,开放平台解决方案项目
  • c# webapi上传、读取、删除图片
  • JAVA面向对象的总结(函数重载与数组)
  • CUBA Studio 9.0 发布 ,企业级应用开发平台
  • Maven 的这 7 个问题你思考过没有?
  • 分享一款快速APP功能测试工具
  • Apache Pulsar 2.1 重磅发布
  • CEF与代理
  • maya建模与骨骼动画快速实现人工鱼
  • mysql_config not found
  • python3 使用 asyncio 代替线程
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 产品三维模型在线预览
  • 简析gRPC client 连接管理
  • 普通函数和构造函数的区别
  • 驱动程序原理
  • 算法系列——算法入门之递归分而治之思想的实现
  • 消息队列系列二(IOT中消息队列的应用)
  • 写代码的正确姿势
  • 学习笔记DL002:AI、机器学习、表示学习、深度学习,第一次大衰退
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • (2)STL算法之元素计数
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (WSI分类)WSI分类文献小综述 2024
  • (翻译)terry crowley: 写给程序员
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • .mysql secret在哪_MYSQL基本操作(上)
  • .net CHARTING图表控件下载地址
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .net core控制台应用程序初识
  • .NET MVC第三章、三种传值方式
  • @column注解_MyBatis注解开发 -MyBatis(15)
  • @WebServiceClient注解,wsdlLocation 可配置
  • [ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务
  • [AIGC codze] Kafka 的 rebalance 机制
  • [android] 天气app布局练习
  • [Arduino学习] ESP8266读取DHT11数字温湿度传感器数据
  • [C++]四种方式求解最大子序列求和问题
  • [codeforces]Levko and Permutation