图搜索基础-深度优先搜索

图搜索基础-深度优先搜索

  • 参考
  • 原理
      • 引入
      • 流程解析
      • 手推例子
  • 代码实现
    • 运行结果
    • 结果分析

参考

理论参考:深蓝学院
实现参考:github项目

原理

引入

对于这样一个图,我们试图找到S到G的通路:
在这里插入图片描述
计算机程序不会像人眼一样,一下子连出一条从S到G的通路,需要一个一个节点的访问。每个节点,在建模和编程的时候都设计为一个数据结构,可以知道跟他直接连通的有哪些节点,以及这些边的代价。
我们假想自己就是程序,在访问一个节点,比如S时,看到S和p,d,e三个节点都有边。那我就可以先看看p是否和G有边,或者先看看d是否和G有边,也可以先看看e是否和G有边,这个暂时不重要。假设我们先看p是否和G有边,我们发现p和q有边,和G无边。这时候我们面临了关键选择:我是沿这条线继续深入,看看q是否和G有边哪?还是回过头开另一条线,看看d是否和G有边?这里的选择就是深度优先和广度优先的全部区别所在。

因为可以认为q是p的下一级节点,继续查看p,就是往深处探寻。就像是面对岔路口一样,我是继续选一个岔路往前走还是回到上一个岔路口,深度优先选择选一个岔路口继续深入,而且每次面对这样的选择都这么做。如果一直走到了死胡同也没到终点,再回到上一个岔路口,换一条路继续深入。

根据这个树形结构,可以更好理解,深度:
在这里插入图片描述
你要说,明明是图结构,为什么又变成树结构?回答是,程序面对岔路口时,并不知道地图全貌(他知道,但他不理解)。

主要的找通路问题解决了,还有两个次要问题:

  1. 我们找通路是为了下次走方便的,所以要记录下来这条通路经过了哪些节点。其实我们只需要记录每一个节点的父节点,只要每个节点只有一个父节点,最后一定能回溯出唯一的一条通路
  2. 我们通常不仅是要找通路,更是要找最短路径。在面临一个节点有多个父节点时,需要比较不同父节点时从起点到这个节点的累计代价和,取代价和最小的父节点为父节点。

流程解析

我们根据图搜索的一个通用模板进行流程解析:

  1. 初始化节点数据结构(节点,父节点,累计代价和)。

  2. 初始化开放列表openlist,把初始节点s包含其中,无父节点,累计代价和为0。

  3. 初始化封闭列表closelist,没有任何东西。

  4. 执行以下循环,直到所有节点被访问或通路被找到,或其他结束标准:
    3.1. 根据算法规则,从openlist中取出一个节点。
    3.2. 根据图结构,获得该节点的相邻节点(和该节点有边的节点),并排除掉在closelist中的节点
    3.3 领域查询:

    • 如果相邻节点不在openlist中,以该节点为父节点,计算累计代价值,将此相邻节点直接加入openlist。
    • 如果相邻节点在openlist中,则假设以该节点为父节点,计算此时累计代价值,并与openlist中的历史累计代价值比较:
      • 如果此时累计代价值大,则不加入openlist。
      • 反之将之前的剔除openlist,把此时的加入openlist。

    3.4. 把该节点放入closelist。
    3.5. 判断该节点是否为终止节点。

  5. 终止节点的累计代价就是整条通路的累计代价;从终止节点开始查询父节点就找到了从初始节点到终止节点的整条通路。

图搜索的核心在于3.1的算法规则。对于深度优先算法,我们应该提取最近加入的节点。
举个例子就是:当我们面对岔路时,我们应该走刚看到的岔路中的一条,而不是之前看到的岔路中的一条。
这个思想直接对应于一种数据结构:堆栈。所以用堆栈管理openlist就可以实现深度优先搜索。

当然,此时还有一个小小的问题:

  • 当前节点的相邻节点有好几个,他们应该以什么样的顺序压入堆栈哪?毕竟越晚被压入,越早被取出嘛,压入先后是有不同的。这点随意。比如面对一个岔路,可以顺时针编号,可以逆时针编号,这部分的优化不是深度优先算法的工作。

手推例子

道理明白了,我们结合流程,手推一遍算法的实现:
参照这个图,假设起点为S,重点为r(不用G是因为搜索过程太长了):
在这里插入图片描述

  1. 初始化节点数据结构(节点,父节点,累计代价和)。
  2. 初始化开放列表openlist,把初始节点s包含其中,无父节点,累计代价和为0。
  3. 初始化封闭列表closelist,没有任何东西。
  4. 开始循环:
    a. 弹出openlist最上层节点S;查找到邻居节点d,e,p,都不在openlist和closelist中,因此依次放进openlist,此时openlist为p,e,d;将节点S放入closelist;S没有终止节点r,继续;
    b. 弹出openlist最上层节点d,查找到邻居节点e,c,b,openlist中已经有e,但这个例子中没有定义代价,所以无法判断是否替换掉原有的e,暂时不管,依次放进openlist,此时openlist为p,e(s),e(d),c,b;将节点d放入closelist;d没有终止节点r,继续;
    c. 弹出openlist最上层节点b,查找到邻居节点a,放进openlist,此时openlist为p,e(s),e(d),c,a;将节点b放入closelist;b没有终止节点r,继续;
    d. 弹出openlist最上层节点a,查找到没有邻居节点,什么都不放入openlist,此时openlist为p,e(s),e(d),c;将节点a放入closelist;a没有终止节点r,继续;
    e. 弹出openlist最上层节点c,查找到邻居节点a,放进openlist,此时openlist为p,e(s),e(d),a;将节点c放入closelist;c没有终止节点r,继续;
    f. 弹出openlist最上层节点a,查找到没有邻居节点,此时openlist为p,e(s),e(d);将节点a放入closelist;发现a没有终止节点r,继续;
    g. 弹出openlist最上层节点e(d),查找到邻居节点h,r,放进openlist,此时openlist为p,e(s),h,r;将节点e(d)放入closelist;发现e(d)没有终止节点r,继续;
    h. 弹出openlist最上层节点r,查找到邻居节点f,放进openlist,此时openlist为p,e(s),h,f;把r放进closelist,r就是终止节点,循环结束
  5. r父节点是e(d),e(d)父节点是d,d父节点是s,因此通路为s-d-e-r。因为没有定义代价,所以通路累计代价值不知道。

代码实现

核心代码:

class DFS(AStar):"""DFS add the new visited node in the front of the openset"""def searching(self):"""Depth-first Searching.:return: path, visited order"""# 初始化节点数据结构(在其他文件中定义过了)# 因为是栅格地图,节点标识就用坐标标识了# 节点的父节点,由self.PARENT列表维护# 到达此节点的累计代价值,由self.g列表维护# 初始化Openlistself.PARENT[self.s_start] = self.s_start    # 维护节点的父节点;起始点父节点是自己self.g[self.s_start] = 0                    # 到达此节点的累计代价值;起始点累计代价值为0self.g[self.s_goal] = math.infheapq.heappush(self.OPEN, (0, self.s_start))# 把起始点压入openlist# 初始化Closelist(在基类中定义过了)# 和初始的Openlist一样,是个空列表# 循环直到所有节点被遍历完(Openlist空掉)while self.OPEN:# 弹出最近压入Openlist堆栈的节点_, s = heapq.heappop(self.OPEN)# 将弹出节点加入Closelist,不再访问self.CLOSED.append(s)# 如果当前节点就是终点,跳出循环if s == self.s_goal:break# 如果当前节点不是终点,进行邻域查询for s_n in self.get_neighbor(s):# 检查该邻域点是否在closelist中(他给漏掉了,我加上去:)if s_n in self.CLOSED:continue# 以当前节点为父节点的邻域节点的累计代价值 = 当前节点的累计代价值 + 从当前节点到该邻域节点代价值之和# 当前节点的累计代价值在上一次循环中被计算过,所以已知# 从当前节点到该邻域节点代价值之和,在基类中被实现,基于栅格地图哈慢炖距离,如果是障碍物则无穷new_cost = self.g[s] + self.cost(s, s_n)if s_n not in self.g:self.g[s_n] = math.inf# 如果以当前节点为父节点,此邻域节点的累计代价值更小,就把原来的剔除Openlist,把现在的加进去,并更新父节点,和累计代价值if new_cost < self.g[s_n]:  # conditions for updating Costself.g[s_n] = new_cost  # 更新累计代价值self.PARENT[s_n] = s    # 更新父节点# 检查剔除原来在openlist中的记录(他给漏掉了,我加上去:)if s_n in self.OPEN:self.OPEN.remove(s_n)# 把邻域节点压入Openlist# dfs, add new node to the front of the opensetprior = self.OPEN[0][0]-1 if len(self.OPEN)>0 else 0    # 计算新元素在列表中位置(自动的堆栈结构更好,他这个要自己维护)heapq.heappush(self.OPEN, (prior, s_n)) return self.extract_path(self.PARENT), self.CLOSED

以上代码是我在源代码,根据前一节得到的流程伪代码添加了注释和缺失步骤的版本。
可以看到,和之前说的伪代码流程除了循环中几个顺序不一样(无所谓)外,其他完全一致。

运行结果

在这里插入图片描述

结果分析

可以看到,深度优先在面临选择时,会一条路走到黑:在有多个选择时,先一直往左下方向走;当没有选择时,溜着墙边这一条路走。

他这次表现的不好,主要是默认面临多个选择时,先探索左下角。当然你可以改成默认右上角,但这是由于我们知道了全局地图,但算法不知道,如果写死了,下次出发点在右上角,终点在左下角,还是会出现类似情况。最好的办法是让算法知道目标大概在哪个方向,这就是启发式算法做的工作了。如前所述:

当前节点的相邻节点有好几个,他们应该以什么样的顺序压入堆栈哪?毕竟越晚被压入,越早被取出嘛,压入先后是有不同的。这点随意。比如面对一个岔路,可以顺时针编号,可以逆时针编号,这部分的优化不是深度优先算法的工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/708491.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何做代币分析:以 USDT 币为例

作者&#xff1a;lesleyfootprint.network 编译&#xff1a;cicifootprint.network 数据源&#xff1a;USDT Token Dashboard &#xff08;仅包括以太坊数据&#xff09; 在加密货币和数字资产领域&#xff0c;代币分析起着至关重要的作用。代币分析指的是深入研究与代币相关…

DolphinScheduler——工作流实例的生命周期

目录 一、DolphinScheduler架构原理 1.1 系统架构图 1.2 DolphinScheduler核心概念 1.2 创建工作流 1.2.1 如何触发一个工作流实例 1.2.2 任务调度链路监控 1.2.3 Workflow-DAG解析 DAG解析 Dispatch分发流程 Master和Worker的交互过程 1.3 任务运行状态 该篇文章主…

Leetcode : 215. 数组中的第 K 个最大元素

给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 思路&#xff1a;最开始排序算法&…

MySQL 存储过程批量插入总结

功能需求背景&#xff1a;今天接到产品经理核心业务表的数据压测功能&#xff0c;让我向核心业务表插入百万级的业务量数据&#xff0c;我首先想到的办法就是存储过程实现数据的批量 。 由于无法提供核心业务表&#xff0c;本文仅仅提供我刚刚自己创建的表bds_base_user 表做相…

nginx 反向代理 与缓存功能

一 理论说明 &#xff08;一&#xff09;反向代理简介 反向代理&#xff1a;reverse proxy&#xff0c;指的是代理外网用户的请求到内部的指定的服务器&#xff0c;并将数据返回给用户的一种方式&#xff0c;这是用的比较多的一种方式。 即 代理服务机 Nginx 除了可以在企…

算法——滑动窗口之最大连续1的个数、将x减到0的最小操作数、水果成篮

3.最大连续1的个数 题目:. - 力扣&#xff08;LeetCode&#xff09; 题目要求的是给定一个二进制数组 nums 和一个整数 k&#xff0c;如果可以翻转最多 k 个 0 &#xff0c;则返回 数组中连续 1 的最大个数 。 按照题目正面去做,还要替换0,很麻烦 反正我们最后要求的是最长…

YOLOv8改进 | 独家创新篇 | 结合SOTA思想利用双主干网络改进YOLOv8(全网独家创新,最重磅的更新)

一、本文介绍 本文给大家带来的改进机制是结合目前SOTAYOLOv9的思想利用双主干网络来改进YOLOv8(本专栏目前发布以来改进最大的内容,同时本文内容为我个人一手整理全网独家首发 | 就连V9官方不支持的模型宽度和深度修改我都均已提供,本文内容支持YOLOv8全系列模型从n到x均可…

刷题笔记 洛谷 P1162 填涂颜色

思路来自 大佬 hat.openai.com/c/9c30032e-5fb9-4677-8c15-9ea6530dc6db 题目链接 P1162 填涂颜色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路 搜索 首先 在外面围上一圈0开始搜素 因为题目说将封闭区域内的0变成2 我们可以在外面进行搜索 把外面所有可以搜索…

Nginx----高性能的WEB服务端(四)

一、http 协议反向代理 1、反向代理&#xff1a;缓存功能 ​ proxy_cache zone_name | off; 默认off #指明调用的缓存&#xff0c;或关闭缓存机制;Context:http, server, location #zone_name 表示缓存的名称.需要由proxy_cache_path事先定义proxy_cache_key string; #缓存中…

【Redis】深入理解 Redis 常用数据类型源码及底层实现(5.详解List数据结构)

本文是深入理解 Redis 常用数据类型源码及底层实现系列的第5篇&#xff5e;前4篇可移步(&#xffe3;∇&#xffe3;)/ 【Redis】深入理解 Redis 常用数据类型源码及底层实现&#xff08;1.结构与源码概述&#xff09;-CSDN博客 【Redis】深入理解 Redis 常用数据类型源码及底…

BeautifulSoup+xpath+re+css简单复习+新的scrapy的学习

1.BeautifulSoupsoup BeautifulSoup(html,html.parser)all_icosoup.find(class_"DivTable") 2.xpath trs resp.xpath("//tbody[idcpdata]/tr") hong tr.xpath("./td[classchartball01 or classchartball20]/text()").extract() 这个意思是找…

文件拖放到窗体事件

网上的实现1 实现结果 具体实现代码&#xff1a;注意需要使能允许拖拽 public partial class Form1 : Form {public Form1(){InitializeComponent();this.AllowDrop true; //允许拖拽}private void Form1_DragEnter(object sender, DragEventArgs e){this.Text DateTime.No…

一键安装|卸载 mysql 8.2.0 shell脚本

场景&#xff1a;为了在无网、外网 mysql 安装方便&#xff0c;这里分享一个自己编写得 shell脚本 这里以当前最新版 mysql 8.2.0&#xff1b;centos-7 二进制包下载&#xff1a; 下载地址 mysql_install.sh #!/bin/bash # 解压安装包 tar -xf mysql-8.2.0-linux-glibc2.17-x8…

TC3xx SMU、PMIC和Tranceiver的功能安全闭环

目录 1.TLF35584安全状态输出响应对象 1.1 响应ERR 收集到的错误信号 1.2 响应监控功能引发的ROT 1.3 响应看门狗引发的错误 1.4 环境过温引发的错误状态 1.5 为什么设计SSx&#xff1f; 2. 安全状态输出给谁 3.小结 在之前文章里&#xff0c;我们简述了TC3xx SMU如何…

尚硅谷(SpringCloudAlibaba微服务分布式)学习代码Eureka部分

1.项目结构 2.cloud2024 pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.a…

面试笔记系列六之redis+kafka+zookeeper基础知识点整理及常见面试题

Redis redis持久化机制&#xff1a;RDB和AOF Redis 持久化 Redis 提供了不同级别的持久化方式: RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储. AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redi…

IPD(集成产品开发)—核心思想

企业发展到一定阶段就会遇到管理瓶颈&#xff0c;IPD流程是一种高度结构化的产品开发流程&#xff0c;它集成了业界很多优秀的产品开发方法论&#xff0c;像搭积木一样的组合成一种非常有效的流程。如果我们能根据企业的规模和行业特点&#xff0c;对全流程的IPD进行合适的裁剪…

html2canvas + JsPDF.js 导出pdf分页时的问题

问题描述 前一段时间 实现了html2canvas jspdf.js 导出pdf的功能 项目当时没有测试做完就先搁置 最近项目要上线发现分页时问题 这篇文章记录一下之前的bug import html2canvas from html2canvas; import JsPDF from jspdf export function savePdf(el, title) {html2canva…

Google checkstyle实战

概述 CheckStyle检查代码是否符合制定的规范。CheckStyle检查是基于源码的&#xff0c;无需编译&#xff0c;执行速度快。 CheckStyle的主要流程是&#xff1a; 对Java文件进行词法语法分析&#xff0c;生成语法树。载入配置文件&#xff08;checkstyle-metadata.xml以及自定…

【ElfBoard】基于 Linux 的智能家居小项目

大家好&#xff0c;我是 Hello阿尔法&#xff0c;这段时间参与了保定飞凌嵌入式技术有限公司举办的 ElfBoard 共创社招募活动&#xff0c;并有幸成为了一名共创官&#xff0c;官方寄来了一块 ELF 1 开发板&#xff0c;开箱看这里 ELF 1 开箱初体验。 作为共创官&#xff0c;我…