深度优先搜索是什么?DFS的代码实现

DFS与BFS主要区别在于探索方式和数据结构:DFS用栈(或递归)实现深度优先,适合连通性、回溯等问题;BFS用队列实现广度优先,常用于找最短路径。

深度优先搜索是什么?dfs的代码实现

深度优先搜索(DFS)本质上是一种探索图或树结构的策略,它有点像你在一个巨大的迷宫里,选择一条路就一直走到底,直到无路可走才回头,然后尝试另一条未探索的路。它追求的是“深度”,优先探索一个分支的所有可能性。

想象一下,你站在一个岔路口,DFS会选择其中一条路径,然后沿着这条路径一直走下去,直到遇到死胡同或者已经走过的路。这时候,它会“回溯”到上一个岔路口,选择另一条还没走过的路继续深入。这个过程会一直重复,直到所有能走的路都走遍了。实现上,我们通常利用递归的特性来模拟这种“深入”和“回溯”,或者使用一个显式的栈来管理待访问的节点。关键在于,我们需要一个机制来记住哪些节点已经访问过,以免陷入无限循环。

DFS与广度优先搜索(BFS)的主要区别在哪里?

说起DFS,就不得不提它的“兄弟”——广度优先搜索(BFS)。这俩就像是两种截然不同的旅行方式。DFS是个“深度探险家”,一头扎进一个区域,不把这个区域的秘密挖完不罢休。而BFS则更像个“全面普查员”,它会先看看周围所有邻居,确认完第一圈,再去看第二圈的邻居。

技术上讲,最大的不同在于它们管理待访问节点的方式:DFS用的是栈(或递归调用的函数栈),后进先出,所以它总是往最深处走;BFS用的是队列,先进先出,所以它总是先处理“离起点近”的节点。这导致了它们在应用上的偏好:BFS常用来找最短路径(因为它是逐层探索的),而DFS则更擅长解决连通性、拓扑排序、以及一些回溯类问题。没有哪个更好,只有哪个更适合当前的问题。有时候,选择哪一个甚至能直接决定你的算法效率。

深度优先搜索在实际编程中通常有哪些应用场景?

DFS的应用范围其实非常广,远不止是教科书上的图遍历。在我看来,只要是涉及到“探索所有可能性”或者“沿着一条路径走到底”的场景,DFS都有用武之地。

最直观的当然是图和树的遍历,比如你想要访问一个树的所有节点,或者找出图中所有与某个节点连通的节点。再比如,路径查找,虽然不是最短路径,但如果你只是想知道A到B有没有路,DFS就能很快告诉你。

更复杂一点的,像检测图中的环,DFS在遍历时如果发现要访问的节点已经在当前递归路径上,那基本就是有环了。还有拓扑排序,对于有向无环图(DAG),DFS能帮助我们找到一个有效的任务执行顺序。

当然,最让我觉得DFS“性感”的,是它在回溯算法中的应用。像经典的N皇后问题、数独求解器、组合问题、排列问题,这些本质上都是在构建一个解决方案,每一步都尝试一个选择,如果发现此路不通就回溯到上一步,尝试另一个选择。DFS的递归特性完美契合了这种“尝试-回溯”的模式。

如何用Python实现一个典型的深度优先搜索?

实现DFS,通常有两种思路:递归和迭代。递归实现因为其简洁性,通常更受欢迎,也更能体现DFS的“深入”特性。迭代实现则更显式地使用了栈,对于某些场景(比如避免递归深度限制)会有优势。

这里我们以一个简单的图遍历为例,用邻接表表示图。

递归实现:

def dfs_recursive(graph, start_node, visited=None):    if visited is None:        visited = set() # 用集合存储已访问节点,查找效率高    visited.add(start_node)    print(start_node, end=' ') # 访问当前节点    for neighbor in graph.get(start_node, []): # 遍历邻居        if neighbor not in visited:            dfs_recursive(graph, neighbor, visited)# 示例图 (邻接表表示)# 1 -- 2# |    |# 3 -- 4# |# 5graph_example = {    '1': ['2', '3'],    '2': ['1', '4'],    '3': ['1', '4', '5'],    '4': ['2', '3'],    '5': ['3']}print("DFS递归遍历结果:")dfs_recursive(graph_example, '1')print("n")

迭代实现(使用显式栈):

def dfs_iterative(graph, start_node):    visited = set()    stack = [start_node] # 初始栈,放入起始节点    while stack:        current_node = stack.pop() # 弹出栈顶元素,即当前要访问的节点        if current_node not in visited:            visited.add(current_node)            print(current_node, end=' ')            # 将邻居节点(未访问过的)压入栈中            # 注意:这里压入的顺序会影响访问顺序,通常是逆序压入以保持与递归相似的“左优先”            # 或者按照你希望的顺序压入            for neighbor in reversed(graph.get(current_node, [])): # 反转,使得栈顶是“最左”的邻居                if neighbor not in visited:                    stack.append(neighbor)print("DFS迭代遍历结果:")dfs_iterative(graph_example, '1')print("n")

无论是递归还是迭代,核心都是维护一个已访问集合,避免重复处理和死循环。选择哪种实现,更多时候取决于个人偏好和具体问题的约束(比如Python的默认递归深度限制)。

以上就是深度优先搜索是什么?DFS的代码实现的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1516252.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 09:54:37
下一篇 2025年12月20日 09:54:47

相关推荐

  • 如何用JavaScript进行机器学习(使用TensorFlow.js)?

    JavaScript可通过TensorFlow.js在浏览器或Node.js中实现机器学习。1. 通过CDN或npm安装并引入tfjs库;2. 创建线性回归模型,使用tensor1d准备数据,sequential构建网络,compile配置优化器与损失函数,fit训练模型,predict进行预测;3…

    2025年12月20日
    000
  • 如何使用 Decorator 装饰器来增强类的功能并实现元编程?

    装饰器可修饰类和方法,实现功能增强与元编程。通过类装饰器可自动添加repr方法、注册子类等;通过方法装饰器可实现计时、日志、权限控制等功能,结合functools.wraps可保留函数元信息,提升可维护性。 在 Python 中,装饰器(Decorator)不仅能修饰函数,还能用于类和方法,实现功能…

    2025年12月20日
    000
  • 如何理解JavaScript中的尾调用优化?

    尾调用优化(TCO)在JavaScript中因调试困难、引擎兼容性问题及性能权衡未被广泛支持,开发者需通过迭代重写、蹦床函数或异步递归避免栈溢出,而其他语言如Scheme、Haskell则将其作为核心特性实现。 理解JavaScript中的尾调用优化(Tail Call Optimization, …

    2025年12月20日
    000
  • 正确处理 Promise 异常:避免遗漏 Catch 语句

    本文旨在帮助开发者理解和避免 Promise 异常处理中常见的错误。通过分析同步 throw 异常与 Promise 异步 rejected 之间的区别,阐述了在不同场景下正确捕获 Promise 异常的方法。同时,讨论了函数设计中统一错误处理方式的重要性,以提升代码的可维护性和可预测性。 理解同步…

    2025年12月20日
    000
  • 在 VS Code 中格式化 Markdown 代码块内容

    本文介绍了在 VS Code 中格式化 Markdown 文件中代码块内容的几种方法。由于 VS Code 默认使用 Markdown 格式化器处理整个文件,因此需要一些技巧来针对特定语言的代码块进行格式化。本文将介绍临时更改语言模式、使用 “Format Selection With&…

    2025年12月20日
    000
  • 格式化 VS Code 中 Markdown 代码块内容

    本文介绍在 VS Code 中格式化 Markdown 代码块内容的方法,尤其是在代码块包含其他编程语言代码时。由于 VS Code 默认使用 Markdown 格式化器,直接格式化选择区域可能会导致问题。本文将探讨一种临时解决方案,并提供关于功能请求和相关问题的讨论,帮助读者更好地管理和格式化 M…

    2025年12月20日
    000
  • TinyMCE在DOM中重定位后的正确初始化与管理

    本文探讨TinyMCE编辑器在从DOM中移除并重新插入后变得不可用的常见问题。核心解决方案在于,当TinyMCE容器从DOM中移除时,必须同步销毁对应的TinyMCE实例;当容器重新插入DOM后,则需重新初始化TinyMCE。通过正确的实例生命周期管理,可确保编辑器在动态内容场景下的稳定运行。 Ti…

    2025年12月20日
    000
  • 如何用Web NFC实现智能海报的交互体验?

    Web NFC智能海报通过一碰即连的交互方式,实现物理与数字世界的无缝衔接。用户轻触嵌入NFC标签的海报,即可直接打开预设网页,无需扫码或下载App,提升互动效率与沉浸感。核心技术包括NFC标签(如NTAG213/215/216)写入NDEF格式URL、基于HTML/CSS/JavaScript构建…

    2025年12月20日
    000
  • JavaScript中数组与对象属性的辨析与高级处理技巧

    本文深入探讨JavaScript中数组与对象属性的本质区别,纠正了关于“数组值”与“数组属性”的常见误解。强调数组适用于有序、数字索引的数据集合,而普通对象更适合存储带有非数字字符串键的属性。文章详细介绍了如何利用Object.entries()等方法获取并过滤对象的各类属性,并通过示例代码演示了获…

    2025年12月20日
    000
  • 使用 addEventListener 实现按钮点击页面跳转:完整教程

    本文详细介绍了如何利用 JavaScript 的 addEventListener 方法监听按钮点击事件,并在此事件触发时实现页面跳转。教程涵盖了 HTML 结构、JavaScript 代码实现,重点讲解了 window.location.replace() 和 window.location.hr…

    2025年12月20日
    000
  • 自定义Bootstrap国家选择器默认占位文本

    本教程详细介绍了如何在Bootstrap国家选择器(bootstrap-select-country)中设置和自定义“未选择”或默认占位文本。通过利用bootstrap-select组件的title属性,开发者可以轻松地将默认的“Nothing Selected”提示替换为任何自定义文本,从而提升用…

    2025年12月20日
    000
  • JavaScript事件监听器:正确获取表单输入最新值的实践

    本文探讨了在JavaScript事件监听器中,如何正确获取HTML表单输入框的最新值。通过分析console.log直接输出DOM元素可能导致的问题,文章详细介绍了使用Array.from结合映射函数来精确提取元素value属性的解决方案,确保在提交表单数据时,能够获取到用户实时输入的内容,而非初始…

    2025年12月20日
    000
  • JavaScript事件监听器获取表单最新输入值的正确姿势

    在JavaScript中,通过事件监听器获取表单文本输入框的当前值时,直接打印HTML元素对象可能无法显示用户修改后的最新值。这是因为console.log通常展示的是元素的初始DOM表示或属性快照。要获取最新的动态值,必须显式访问元素的value属性。本文将详细阐述这一常见误区,并提供使用Arra…

    2025年12月20日
    000
  • 深入理解JavaScript属性:数组与对象的非数字键处理

    JavaScript中,所有存储的数据本质上都是对象的属性。数组的“值”实际上是其以数字为键的属性,而非数字键的属性则被视为普通对象属性。本文旨在澄清数组与对象属性的根本区别,强调当需要使用非数字键时应优先选择普通对象。我们将探讨如何利用Object.entries()遍历并筛选出对象或类数组结构中…

    2025年12月20日
    000
  • 使用 addEventListener 实现按钮点击页面跳转教程

    本教程详细讲解如何利用JavaScript的addEventListener方法,在用户点击HTML按钮后,安全有效地将页面重定向到另一个指定的URL。文章将涵盖核心的HTML和JavaScript代码实现,重点介绍window.location.replace()或window.location.…

    2025年12月20日
    000
  • JavaScript 事件监听器中获取表单输入最新值的正确姿势

    本文旨在解决JavaScript事件监听器中,通过console.log直接输出HTML元素集合时,无法获取表单输入字段最新用户修改值的问题。核心在于理解HTML属性与DOM属性的区别,并指导开发者如何正确地访问和提取输入元素的当前value属性,从而实现动态数据的准确提交。 理解HTML属性与DO…

    2025年12月20日
    000
  • JavaScript中获取表单输入最新值的实践指南

    在JavaScript中,当通过事件监听器获取表单输入值时,有时会错误地获取到元素的初始HTML属性值而非用户修改后的最新值。本文将深入探讨HTML属性与DOM属性的差异,并提供一种可靠的方法,通过直接访问元素的value属性来确保获取到表单输入字段的实时、最新数据,从而避免常见的开发陷阱。 1. …

    2025年12月20日
    000
  • 优化Bootstrap Country Picker:配置默认“未选择”选项

    本文详细介绍了如何在Bootstrap Country Picker组件中配置默认的“未选择”状态,解决组件初始化时无明确空选提示的问题。通过利用title属性,开发者可以轻松自定义下拉菜单的占位符文本,从而提升用户体验和表单的清晰度,确保用户在提交前能明确看到未选择的提示。 理解Bootstrap…

    2025年12月20日
    000
  • JS 生成器与迭代器协议 – 实现自定义可迭代对象的完整指南

    JavaScript的生成器与迭代器协议使自定义数据结构可被for…of遍历,核心是实现Symbol.iterator方法并返回具备next()的迭代器,生成器函数因自动满足该协议且能按需产出值,成为实现惰性求值、处理无限序列和构建数据流管道的理想选择。 JavaScript的生成器(G…

    2025年12月20日
    000
  • JavaScript事件监听器中获取表单输入实时值的方法

    本文旨在解决JavaScript事件监听器在获取表单输入值时,默认显示初始HTML属性而非用户当前输入值的常见问题。通过深入理解DOM元素属性与HTML属性的区别,我们将展示如何正确地通过访问元素的.value属性来获取实时数据,并提供使用Array.from进行高效数据提取的示例代码,确保在提交表…

    2025年12月20日
    000

发表回复

登录后才能评论
关注微信