一文搞懂bfs,dfs和高级图算法
你以为BFS(广度优先搜索)和DFS(深度优先搜索)这两种基础算法,简单到小学数学就能搞定?但真的是这样吗?很多人都这么认为,但真的对吗?今天,我们不只是走马观花般看看这些算法,而是要挖掘出它们背后隐藏的秘密,探究那些被忽略的细节,看看它们是如何在不同场景中大显身手的!
BFS(广度优先搜索)
BFS是什么?很多人觉得它就是一层一层地访问节点,像剥洋葱一样,从根节点到离根节点最远的节点。听起来简单,但这背后隐藏的复杂性可能让你大吃一惊!BFS的真正威力,在于它如何在短时间内找到目标,像是一支箭直达目标。
如何运作?
- 起点:从根节点开始,首先访问它,并将其放入队列中。
- 层层推进:每次从队列中取出一个节点,访问它的所有邻居(还没访问过的),并将这些邻居加入队列。
- 结束:队列为空时,整个搜索过程结束。
示例代码(Python):
from collections import dequedef bfs(graph, start):visited = set()queue = deque([start])visited.add(start)while queue:node = queue.popleft()print(node)for neighbor in graph[node]:if neighbor not in visited:queue.append(neighbor)visited.add(neighbor)# 示例图表示为邻接表
graph = {'A': ['B', 'C'],'B': ['D', 'E'],'C': ['F'],'D': [],'E': ['F'],'F': []
}bfs(graph, 'A')
DFS(深度优先搜索)
“深度”优先搜索,听上去像是一头扎进了无底深渊,不达目的不罢休。DFS的特点是它像一位执着的探险家,深挖到树或图的最深处,直到无法再深入,然后才回头。它擅长解决那些需要全面探索的问题,但你要小心陷入无尽的循环!
如何运作?
- 起点:同样从根节点开始访问。
- 深度探索:选择一个未访问的邻居,继续递归地访问它,直到走到死胡同。
- 回溯:没有未访问的邻居时,回到前一个节点,继续寻找其他未探索的路径。
- 结束:所有节点都被访问过时,整个搜索结束。
示例代码(Python):
def dfs(graph, node, visited=None):if visited is None:visited = set()visited.add(node)print(node)for neighbor in graph[node]:if neighbor not in visited:dfs(graph, neighbor, visited)# 示例图表示为邻接表
graph = {'A': ['B', 'C'],'B': ['D', 'E'],'C': ['F'],'D': [],'E': ['F'],'F': []
}dfs(graph, 'A')
你可能一直认为BFS和DFS是对立的,一旦用错了,问题就无解了。但真的是这样吗?其实,这两种算法就像双刃剑,在特定场景中都有各自的优劣势。BFS适合寻找最短路径,而DFS在某些场景下能节省大量时间!
BFS和DFS的优劣
BFS的优势:
- 找到最短路径:在无权图中,BFS总能找到从起点到终点的最短路径。
- 层次分明:BFS可以按照距离进行分层遍历,适合解决很多层次结构的问题。
BFS的劣势:
- 内存占用高:BFS需要维护一个队列,空间复杂度相对较高,尤其是在大规模图中。
- 不适合深度搜索:当需要探索较深的层次时,BFS的效率较低。
DFS的优势:
- 内存占用少:DFS采用递归或栈来维护状态,空间复杂度较低。
- 探索深层次:适合用来找出所有可能的路径,比如解决迷宫问题,或是进行回溯算法。
DFS的劣势:
- 无法保证最短路径:DFS可能会先探索一条较长的路径,而忽略了更短的路径。
- 容易陷入死循环:如果图中存在环,DFS在没有适当处理的情况下,可能会无限循环。
适用场景对比
- BFS适用场景:如果你要解决最短路径问题,或是分层结构清晰的问题,BFS是你的首选,比如无权图的路径搜索、广度遍历树结构等。
- DFS适用场景:当你需要全面探索或者递归问题时,比如迷宫探路、拓扑排序、解决全排列问题,DFS是你的利器。
既然我们已经深入BFS和DFS的内部,接下来就让我们更进一步,看看它们与其他常见算法相比,究竟有何独到之处。你以为算法都大同小异?但真的是这样吗?许多人都在盲目选择,但选择错误往往会导致性能崩溃!
Dijkstra算法
Dijkstra算法,听起来像是个复杂高深的家伙,但它的本质其实就是一个贪婪的小精灵,总是优先选择当前最短路径的节点。它可以看作是BFS的进阶版,只不过它引入了一个重量级的新角色——权重。
如何运作?
- 起点:从起始节点开始,初始化距离为0,其他节点的距离为无穷大。
- 选择最近:每次选择当前距离最短的节点进行扩展,并更新邻居节点的距离。
- 重复直到结束:一旦所有节点的最短路径都确定,算法结束。
示例代码(Python):
import heapqdef dijkstra(graph, start):priority_queue = [(0, start)]distances = {node: float('infinity') for node in graph}distances[start] = 0while priority_queue:current_distance, current_node = heapq.heappop(priority_queue)if current_distance > distances[current_node]:continuefor neighbor, weight in graph[current_node]:distance = current_distance + weightif distance < distances[neighbor]:distances[neighbor] = distanceheapq.heappush(priority_queue, (distance, neighbor))return distances# 示例图表示为邻接表,包含权重
graph = {'A': [('B', 1), ('C', 4)],'B': [('D', 2), ('E', 5)],'C': [('F', 3)],'D': [],'E': [('F', 1)],'F': []
}distances = dijkstra(graph, 'A')
print(distances)
A*算法
如果Dijkstra是一位稳扎稳打的战士,那么A*(A-star)就是一个充满智慧的战略家,它不仅考虑当前的代价,还会预估未来的“步数”。这种“聪明”的算法在寻路问题中表现得尤为出色。
你以为A算法只是Dijkstra的升级版,只是多了点“聪明”的预估功能?但真的是这样吗?很多人都这么认为,但真的对吗?A算法可不仅仅是加了点调料,它是在迷宫、路径规划等问题中的一把“利刃”,精准、迅速地找到最优解。接下来,我们就来深入了解A*算法,看看它是如何比其他算法更具优势的!
A算法是什么?它不仅考虑当前的路径代价,还引入了一个“启发式函数”(heuristic function),来预估从当前节点到目标节点的代价,从而在保证找到最优解的前提下,尽可能减少搜索的范围。换句话说,A算法总是朝着最有希望的方向前进,它知道自己要去哪,并且不会走冤枉路。
如何运作?
- 起点:从起始节点开始,计算其实际代价(从起点到当前节点的路径长度)和预估代价(从当前节点到目标节点的估计距离)的总和。
- 选择最优路径:每次从未访问的节点中,选择实际代价与预估代价之和最小的节点进行扩展。
- 重复直到结束:当扩展到目标节点时,路径确定,算法结束。
启发式函数(Heuristic Function)
启发式函数是A*算法的核心,它预测了当前节点到目标节点的距离,常见的启发式函数包括:
- 曼哈顿距离(Manhattan Distance):适用于网格地图的水平或垂直移动。
- 欧几里得距离(Euclidean Distance):适用于可以在任意方向移动的情况。
- 切比雪夫距离(Chebyshev Distance):适用于允许对角线移动的情况。
示例代码(Python):
import heapqdef heuristic(a, b):# 曼哈顿距离作为启发式函数return abs(a[0] - b[0]) + abs(a[1] - b[1])def a_star(graph, start, goal):# 优先队列(最小堆)priority_queue = [(0, start)]# 跟踪路径代价g_costs = {start: 0}# 跟踪路径came_from = {start: None}while priority_queue:current_cost, current_node = heapq.heappop(priority_queue)if current_node == goal:# 构造最终路径path = []while current_node:path.append(current_node)current_node = came_from[current_node]return path[::-1]for neighbor, cost in graph[current_node]:tentative_g_cost = g_costs[current_node] + costif neighbor not in g_costs or tentative_g_cost < g_costs[neighbor]:g_costs[neighbor] = tentative_g_costf_cost = tentative_g_cost + heuristic(neighbor, goal)heapq.heappush(priority_queue, (f_cost, neighbor))came_from[neighbor] = current_nodereturn None # 未找到路径# 示例图表示为邻接表,包含权重
graph = {(0, 0): [((1, 0), 1), ((0, 1), 1)],(1, 0): [((1, 1), 1), ((0, 0), 1)],(0, 1): [((1, 1), 1), ((0, 0), 1)],(1, 1): [((1, 0), 1), ((0, 1), 1), ((2, 1), 1)],(2, 1): [((1, 1), 1)]
}start = (0, 0)
goal = (2, 1)
path = a_star(graph, start, goal)
print("A* Path:", path)
比较BFS、DFS与A*的区别与优势
A*算法的优势:
- 高效性:A*不仅利用了当前路径的代价,还考虑了未来的预估代价,因此能快速找到最优路径,减少不必要的搜索。
- 灵活性:可以根据不同的场景选择不同的启发式函数,使其适用于多种路径规划问题。
A*算法的劣势:
- 依赖启发式函数:如果启发式函数选择不当,可能会导致算法效率低下,甚至无法找到最优解。
- 内存消耗大:由于需要维护较多的状态,A*在内存方面的消耗较大,尤其是在复杂图或大规模搜索空间中。
BFS vs DFS vs A*:
- BFS:最适合无权图中的最短路径搜索,缺点是需要大量内存,处理大图时不适用。
- DFS:适用于需要完全探索的场景,比如回溯问题,优点是内存消耗较低,但无法保证最优解。
- A*:在有权图中表现最佳,特别适合路径规划问题,既能保证最优解,又能通过合理的启发式函数提高效率。
结论
你以为只要掌握了BFS和DFS,算法的世界就尽在掌握?但真的是这样吗?在实际应用中,别让传统的思维束缚了你的算法选择,你将有能力所向披靡!
相关文章:
一文搞懂bfs,dfs和高级图算法
你以为BFS(广度优先搜索)和DFS(深度优先搜索)这两种基础算法,简单到小学数学就能搞定?但真的是这样吗?很多人都这么认为,但真的对吗?今天,我们不只是走马观花…...
【Rust光年纪】Rust异步编程利器:异步DNS、高性能Web服务器一网打尽
构建高效网络应用必备:解读Rust异步编程神器 前言 Rust 是一种快速流行的系统编程语言,它以其内存安全和并发性能而闻名。在 Rust 生态系统中,有许多优秀的库和框架可以帮助开发者构建高性能、可靠的应用程序。本文将介绍几个在 Rust 中备受…...
04学生管理系统(栈)
文章目录 预处理菜单结构体主函数函数声明栈操作功能实现 预处理 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<windows.h> #include<conio.h>#define OVERFLOW -2 #define FALSE 0 #define TRUE 1 #define OK 1 …...
我们如何在centos上部署批量管理工具ansible
1)我们先准备环境、设备 #我们准备一台服务机 (192.168.61.140) #然后准备几天客户机(192.168.61.141 192.168.61.142)这里我们准备两台2)然后我们在客服务机里面添加域名 vi /etc/hosts #添加如下内容 192.…...
如何评估前端代码审查培训计划的有效性?
评估前端代码审查培训计划的有效性可以通过以下方法: 培训前后测试: 在培训前后对学员进行测试,比较结果以评估知识增长。 学员反馈: 通过问卷调查、访谈或开放式反馈收集学员对培训内容、方式和效果的看法。 参与度:…...
使用nvm切换Node.js版本
一、安装nvm nvm(Node Version Manager)是一个用于管理Node.js版本的工具,它允许你在同一台机器上安装和切换多个Node.js版本。 1.安装nvm https://github.com/coreybutler/nvm-windows 访问以上链接到github去下载 点击releases 下载下图…...
x264 编码器 PSNR算法源码分析
PSNR PSNR(Peak Signal-to-Noise Ratio,峰值信噪比)是一种常用的图像质量评价指标,用于衡量图像或视频的清晰度和质量。PSNR是基于信号的最大可能功率与影响信号的噪声功率之间的比率。在图像处理领域,PSNR通常用来评估图像压缩或图像增强算法的效果。 PSNR的计算公式是…...
开源web版3D展示工具Online3DViewer
Online3DViewer是一个免费且开源的Web解决方案,它允许用户在浏览器中直接预览和探索3D模型。 以下是关于Online3DViewer的详细介绍: 一、基本概述 定义:Online3DViewer是一个在线3D模型查看器,支持多种3D文件格式,用…...
白骑士的Matlab教学实战项目篇 4.2 信号与图像处理项目
系列目录 上一篇:白骑士的Matlab教学实战项目篇 4.1 数据分析与可视化 信号处理和图像处理是 MATLAB 的重要应用领域,广泛应用于医学、工程、科学研究等领域。以下内容将介绍信号滤波与频域分析、图像增强与分割的基本概念和方法,并通过一个…...
复现、并改进open-mmlab的mmpose详细细节
复现open-mmlab的mmpose详细细节 1.配置环境2.数据处理3.训练4.改进mmpose4.1 快速调试技巧4.2 快速定位4.3 改进backbone4.3.1 使用说明4.3.2 改进案例4.3.2.1 复现mmpose原配置文件4.3.2.2 复现开源项目4.3.2.3 修改配置文件4.3.2.4 修改新模型 4.4 添加auxiliary_head4.4.1 …...
编写兼容Python2.x与3.x代码
编写兼容Python2.x与3.x代码 当我们正处于Python2.x到Python3.x的过渡期时,你可能想过是否可以在不修改任何代码的前提下能同时运行在Python2和3中。这看起来还真是一个合理的诉求,但如何开始呢?哪些Python2代码在3.x解释器执行时容易出状况…...
比特币8.12学习问题
疑问:什么是过滤,什么是offset 没有投钱的情况下,怎么用api 公式:单币分配金额 总资金 / 2/ offset/选币数量,其中2 表示多空 买入滑点(Slippage)是指在执行交易订单时,实际成交…...
解析 Vue 中的app.version、 app.provide 与 app.runWithContext :原理、应用与实例剖析
目录 app.provide app.runWithContext app.version 非 VIP 用户能够通过积分下载博文资源 app.provide 在 Vue 3.0 中,app.provide充当着在应用层级提供全局共享数据或者服务的关键角色。 app.provide(key, value) 这一方法接收两个关键参数,其中 …...
Ubuntu server 命令行跑selenium
背景 自动化测试都是在本机win上使用selenium 跑自动化脚本,但是服务器都是命令行的没有web界面 依赖包部署 apt-get install zlib1g-dev zlib1g## 安装谷歌浏览器 ## 跳到底部,选择其他平台 https://www.google.com/chrome/## ubuntu # dpkg -i google-chrome-stable_…...
刚刚,模糊测试平台SFuzz受到行业认可
近日,中国网络安全产业联盟(CCIA)正式发布了“2024年网络安全优秀创新成果大赛-安全严选专题赛”评选结果,开源网安模糊测试平台SFuzz凭借重大创新能力,得到组委会认可,获本次大赛创新产品优胜奖。 2024年网…...
数据结构与算法——DFS(深度优先搜索)
算法介绍: 深度优先搜索(Depth-First Search,简称DFS)是一种用于遍历或搜索树或图的算法。这种算法会尽可能深地搜索图的分支,直到找到目标节点或达到叶节点(没有子节点的节点),然后…...
基于lambda简化设计模式
写在文章开头 本文将演示基于函数式编程的理念,优化设计模式中繁琐的模板化编码开发,以保证用尽可能少的代码做尽可能多的事,希望对你有帮助。 Hi,我是 sharkChili ,是个不断在硬核技术上作死的 java coder ÿ…...
揭秘! 经纬恒润“车路云一体化”方案研发服务背后的科技驱动力
随着高级别智能驾驶技术的飞速发展,自动驾驶与路侧基础设施协同合作已成为行业内的又一热点。我国率先提出以“车路云一体化”为核心的战略布局,国家政策密集出台,地方试点积极推进,行业标准日趋完善,智能网联汽车“车…...
Redis操作--RedisTemplate(二)StringRedisTemplate
一、介绍 1、简介 由于存储在 Redis 中的 key 和 value 通常是很常见的 String 类型,Redis模块提供了 RedisConnection 和 RedisTemplate 的扩展,分是 StringRedisConnection 和 StringRedisTemplate,作为字符串操作的解决方案。 通过源码…...
【自动驾驶】ROS中自定义格式的服务通信,含命令行动态传参(c++)
目录 通信流程创建服务器端及客户端新建服务通讯文件修改service的xml及cmakelistCMakeLists.txt编辑 msg 相关配置编译消息相关头文件在cmakelist中包含头文件的路径在service包下编写service.cpp在client包下编写client.cpp测试运行查询服务的相关指令列出目前的所有服务&…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 (TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...
Vue3中的computer和watch
computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...
