【Python搜索算法】深度优先搜索(DFS)算法原理详解与应用,示例+代码

news2024/11/29 4:01:55

目录

1 基本原理

2 DFS算法流程

3 时间复杂度

4 空间复杂度

5 DFS算法应用案例:

5.1 解决路径查找问题 

5.2 解决图的连通性问题

5.3  拓扑排序

5.4  在树结构中进行深度遍历


深度优先搜索(DFS)是一种重要的图遍历算法,用于探索图中的节点和边。

1 基本原理

  • DFS 是一种递归或栈(堆栈)数据结构的算法,用于图的遍历。
  • 从一个起始节点开始,尽可能深入图的分支,直到无法继续深入,然后回溯并探索其他分支。
  • 通过标记已访问的节点来避免重复访问。

2 DFS算法流程

  1. 创建一个空的栈(Stack)数据结构,用于存储待访问的节点。

  2. 从起始节点开始,将其标记为已访问并入栈。

  3. 重复以下步骤,直到栈为空: a. 出栈一个节点,并标记为已访问。 b. 检查该节点的所有未被访问的邻居节点。 c. 对于每个未访问的邻居节点,将其标记为已访问并入栈。

  4. 如果无法再继续,即没有未访问的邻居节点,返回上一个节点并继续。

  5. 重复步骤2-4,直到遍历整个图。

3 时间复杂度

  • 在最坏情况下,DFS的时间复杂度可以是O(V + E),其中V是节点数,E是边数。
  • 由于DFS可能访问整个图,因此在稠密图中可能效率较低。

4 空间复杂度

  • 空间复杂度取决于递归深度或堆栈的大小,通常为O(V) 

5 DFS算法应用案例:

5.1 解决路径查找问题 

        一个常见的应用案例是查找从起始节点到目标节点的路径。例如,在以下示例图中,我们要查找从节点A到节点G的路径。

下面是一个简单的Python代码示例,用于执行DFS算法,找到从节点A到节点G的路径。

# 定义示例图
GRAPH = {
    'A': ['B', 'C'],
    'B': ['D', 'E'],
    'C': ['F'],
    'D': [],
    'E': ['F'],
    'F': ['G'],
    'G': []
}

# 定义DFS算法,查找从起始节点到目标节点的路径
def dfs(graph, start, end, path=[]):
    # 将当前节点添加到路径中
    path = path + [start]

    # 如果当前节点等于目标节点,返回找到的路径
    if start == end:
        return path

    # 如果当前节点不在图中,返回None
    if start not in graph:
        return None

    # 遍历当前节点的邻居节点
    for node in graph[start]:
        # 如果邻居节点不在已访问的路径中,继续DFS
        if node not in path:
            new_path = dfs(graph, node, end, path)
            # 如果找到路径,返回该路径
            if new_path:
                return new_path

    # 如果无法找到路径,返回None
    return None

# 调用DFS算法查找从A到G的路径
path = dfs(GRAPH, 'A', 'G')
if path:
    print("Path from A to G:", path)
else:
    print("No path found.")

输出:

5.2 解决图的连通性问题:查找下图中的连通组件

import networkx as nx
import matplotlib.pyplot as plt

# 定义一个有向图的邻接列表表示
graph = {
    'A': ['B', 'C'],
    'B': ['A'],
    'C': ['A'],
    'D': ['E'],
    'E': ['D'],
    'F': [],
}

def find_connected_components(graph):
    def dfs(node, component):
        visited.add(node)
        component.append(node)
        for neighbor in graph.get(node, []):
            if neighbor not in visited:
                dfs(neighbor, component)

    visited = set()
    connected_components = []

    for node in graph:
        if node not in visited:
            component = []
            dfs(node, component)
            connected_components.append(component)

    return connected_components

# 查找连通组件
components = find_connected_components(graph)

# 打印连通组件
for i, component in enumerate(components, start=1):
    print(f"Connected Component {i}: {component}")

# 创建有向图
G = nx.DiGraph(graph)

# 绘制图形
pos = nx.spring_layout(G)
nx.draw(G, pos, with_labels=True, node_size=500, node_color='lightblue', font_size=10, font_color='black', font_weight='bold')

# 添加边的标签
labels = {}
for node in G.nodes():
    labels[node] = node
edge_labels = {(u, v): v for u, v in G.edges()}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=8)

plt.show()

输出:

          示例创建 find_connected_components 函数,用于查找图中的连通组件。它使用深度优先搜索(DFS)来遍历图,找到连通组件,并将它们添加到 connected_components 列表中。解析如下:

  1. find_connected_components(graph) 函数接受一个有向图的邻接列表表示为输入,并返回图中的连通组件。

  2. 内部嵌套的 dfs(node, component) 函数是深度优先搜索函数。它采用两个参数:

    • node 表示当前遍历的节点。
    • component 是一个列表,用于存储当前连通组件中的节点。

    dfs 函数的目标是遍历与 node 相关联的节点,并将它们添加到 component 中。

  3. visited 是一个集合,用于跟踪已访问的节点。一开始,它是空的。

  4. connected_components 是一个列表,用于存储找到的连通组件。开始时,它也是空的。

  5. 外部的 for 循环遍历图中的每个节点,以确保所有节点都被覆盖。对于每个节点,它执行以下操作:

    • 如果该节点尚未在 visited 中,表示它是一个新的连通组件的起始节点。
    • 创建一个新的空列表 component,用于存储该连通组件的节点。
    • 调用 dfs(node, component) 函数,开始深度优先搜索,并将所有与该节点相连的节点添加到 component 中。
    • component 添加到 connected_components 中,表示已找到一个连通组件。
  6. 最后,函数返回 connected_components 列表,其中包含了所有找到的连通组件。

5.3  拓扑排序

        拓扑排序是用于确定有向图中节点的线性顺序,使得图中的每一条有向边都是从前面的节点指向后面的节点。在拓扑排序中,没有环路存在。

应用示例

        假设有一个有向图如下,表示课程之间的依赖关系,您需要找到一个可以完成所有课程的顺序。如果存在环路,表示存在无法解决的依赖关系,您需要找到一个没有环路的顺序。

import networkx as nx
import matplotlib.pyplot as plt

def topological_sort(graph):
    def dfs(node):
        visited.add(node)
        for neighbor in graph.get(node, []):
            if neighbor not in visited:
                dfs(neighbor)
        result.append(node)

    visited = set()
    result = []

    for node in graph:
        if node not in visited:
            dfs(node)

    return result[::-1]

# 定义有向图的依赖关系
courses = {
    'CSC300': ['CSC100', 'CSC200'],
    'CSC200': ['CSC100'],
    'CSC100': [],
    'CSC400': ['CSC300', 'CSC200'],
}

# 创建一个有向图
G = nx.DiGraph(courses)

# 调用拓扑排序算法
topological_order = topological_sort(courses)

if topological_order:
    print("Topological Order of Courses:", topological_order)
else:
    print("No valid topological order (contains a cycle).")

# 绘制有向图
pos = nx.spring_layout(G)
nx.draw(G, pos, with_labels=True, node_size=500, node_color='lightblue', font_size=10, font_color='black', font_weight='bold')

# 添加边的标签
labels = {}
for node in G.nodes():
    labels[node] = node
edge_labels = {(u, v): v for u, v in G.edges()}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=8)

plt.show()

输出为:

        示例定义了topological_sort函数,用于执行拓扑排序。这个函数使用深度优先搜索(DFS)来查找图的拓扑排序。如果图中存在环路,该函数仍然会返回一个排序结果,但它不保证是一个有效的拓扑排序。

5.4  在树结构中进行深度遍历

import networkx as nx
import matplotlib.pyplot as plt

class TreeNode:
    def __init__(self, value):
        self.value = value
        self.children = []

    def add_child(self, child_node):
        self.children.append(child_node)

def depth_first_search(node, graph, parent=None):
    if node is None:
        return

    graph.add_node(node.value)  # 添加节点到图中

    if parent is not None:
        graph.add_edge(parent.value, node.value)  # 添加边连接父节点和当前节点

    print(node.value)  # 在DFS时输出节点值

    for child in node.children:
        depth_first_search(child, graph, node)  # 递归遍历子节点

# 创建一个较复杂的树结构
root = TreeNode("A")
b = TreeNode("B")
c = TreeNode("C")
d = TreeNode("D")
e = TreeNode("E")
f = TreeNode("F")
g = TreeNode("G")
h = TreeNode("H")
i = TreeNode("I")

root.add_child(b)
root.add_child(c)
b.add_child(d)
b.add_child(e)
c.add_child(f)
c.add_child(g)
g.add_child(h)
h.add_child(i)

# 创建一个有向图
G = nx.DiGraph()

# 执行深度优先搜索并创建图
depth_first_search(root, G)

# 绘制树结构图
pos = nx.spring_layout(G)
nx.draw(G, pos, with_labels=True, node_size=500, node_color='lightblue', font_size=10, font_color='black', font_weight='bold')

# 添加边的标签
labels = {}
for node in G.nodes():
    labels[node] = node
edge_labels = {(u, v): v for u, v in G.edges()}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=8)

plt.show()

         这段代码用于创建一个树结构,然后执行深度优先搜索(DFS),最后绘制树结构图并添加标签。以下是对代码的详细解析:

  1. 首先,定义了一个树结构的节点类 TreeNode,其中每个节点具有一个值和子节点列表。

  2. depth_first_search 函数中,执行深度优先搜索。它接受三个参数:

    • node:当前要处理的节点。
    • graph:用于构建树结构图的 NetworkX 有向图对象。
    • parent:父节点,用于添加边。

    在函数中,执行以下操作:

    • 添加当前节点到图中(graph.add_node(node.value))。
    • 如果存在父节点,添加从父节点到当前节点的边(graph.add_edge(parent.value, node.value))。
    • 打印当前节点的值,以在DFS期间输出节点值。
    • 递归遍历当前节点的子节点,使用当前节点作为父节点。
  3. 创建一个较复杂的树结构:

    • 根节点为 "A",有两个子节点 "B" 和 "C"。
    • 节点 "B" 有两个子节点 "D" 和 "E"。
    • 节点 "C" 有两个子节点 "F" 和 "G"。
    • 节点 "G" 有一个子节点 "H"。
    • 节点 "H" 有一个子节点 "I"。
  4. 创建一个 NetworkX 有向图对象 G,用于存储树结构图。

  5. 执行深度优先搜索,从根节点 "A" 开始。深度优先搜索会递归遍历树的每个分支,并在DFS期间输出节点值。

  6. 使用 NetworkX 绘制树结构图:

    • nx.spring_layout 用于确定节点的位置。
    • nx.draw 用于绘制节点和边,设置节点的大小、颜色和标签。
    • nx.draw_networkx_edge_labels 用于添加边的标签。
  7. 最后,通过 plt.show() 显示绘制的树结构图。

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

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

相关文章

容性耦合和距离的关系

容性耦合和距离的关系 在电路板设计中,耦合是指两个或多个电路元件之间的相互作用,这种相互作用会影响到电路的性能。而容性耦合是指通过电场进行耦合的一种方式,它可以通过电容器的作用使得信号在电路板中传递,但同时也会带来一些…

HBuilder创建uniapp默认项目导入uview(胎教)

1:更新HBuilder 建议更新 2:更新插件 我本人在没有更新插件的情况下报错了,找到了**这个大佬**解决问题,所以建议更新插件 先卸载uni-app(Vue2)编译 再重新安装 uni-app(Vue2)…

RabbitMQ从0到1完整学习笔记二:《高级篇》

目录 1. 发送者的可靠性 1.1.生产者连接重试机制 1.2.生产者确认机制(发布确认) 1.3.实现生产者确认 1.3.1.开启生产者确认 1.3.2.定义ReturnCallback 1.3.3.定义ConfirmCallback 拓展: confirm 模式细节处理 2.MQ的可靠性 2.1.数据持久化 2.…

论文阅读:Seeing in Extra Darkness Using a Deep-Red Flash

论文阅读:Seeing in Extra Darkness Using a Deep-Red Flash 今天介绍的这篇文章是 2021 年 ICCV 的一篇 oral 文章,主要是为了解决极暗光下的成像问题,通过一个深红的闪光灯补光。实现了暗光下很好的成像效果,整篇文章基本没有任…

Hadoop3教程(十一):MapReduce的详细工作流程

文章目录 (94)MR工作流程Map阶段Reduce阶段 参考文献 (94)MR工作流程 本小节将展示一下整个MapReduce的全工作流程。 Map阶段 首先是Map阶段: 首先,我们有一个待处理文本文件的集合; 客户端…

Flash-Attention

这是一篇硬核的优化Transformer的工作。众所周知,Transformer模型的计算量和储存复杂度是 O ( N 2 ) O(N^2) O(N2) 。尽管先前有了大量的优化工作,比如LongFormer、Sparse Transformer、Reformer等等,一定程度上减轻了Transformer的资源消耗…

[Zookeeper:基于容器化]:快速部署安装

文章目录 一:拉取docker zookeeper镜像二:创建zookeeper宿主机data目录三:运行容器四:开放防火墙 一:拉取docker zookeeper镜像 [rootvboxnode3ccccccttttttchenyang bigdata]# docker pull zookeeper Using default …

IDEA Gradle Lombok错误:找不到符号 setter getter方法没有

今天IDEA2022.3.3开个Gradle(7.5版本)项目,加入了Lombok依赖,依赖如下: dependencies {implementation group: org.springframework.boot, name: spring-boot-starter-web, version: 3.1.4compileOnly group: org.pro…

Windows:VS Code IDE安装ESP-IDF【保姆级】

物联网开发学习笔记——目录索引 参考: VS Code官网:Visual Studio Code - Code Editing. Redefined 乐鑫官网:ESP-IDF 编程指南 - ESP32 VSCode ESP-ID Extension Install 一、前提条件 Visual Studio Code IDE安装ESP-IDF扩展&#xf…

基于nodejs+vue水浒鉴赏平台系统

该平台内容丰富,便于操作。 零碎化的信息带给我们的短暂快感,人们沉寂在网络游戏、短视频、网络聊天,沉寂其中无法自拔。脱离现实、胡编乱造、五花八门的网络小说, 优秀的文化传承,学习和借鉴才是当今社会需要的。 …

睡衣内衣服装商城小程序的作用是什么

服装行业一直都是市场很重要的组成部分,每个人都需要,且根据品牌、样式作用等可以细分很多类目,其中睡衣内衣也有不小的市场规模,从业商家多、市场需求度高。 但同时睡衣内衣经营痛点也比较明显。 当今消费者习惯于线上消费&…

【面试经典150 | 区间】汇总区间

文章目录 Tag题目来源题目解读解题思路方法一:一次遍历复杂度分析 其他语言python3C 写在最后 Tag 【一次遍历】【数组】【字符串】 题目来源 228. 汇总区间 题目解读 给定一个无重复的升序数组 nums,需要将这个数组按照以下规则进行汇总&#xff1…

报道 | 2023-2024年1月国际运筹优化会议汇总

2023年10月、11月、12月召开会议汇总: 2023 International Conference on Optimization and Applications (ICOA) Location: Abu Dhabi, United Arab Emirates Important dates: Conference: October 05-06, 2023 Details: https://lct.ac.ae/en/icoa/ 2023 INF…

Kafka:容器安装篇

文章目录 一:拉取 kafka镜像: docker pull bitnami/kafka二:运行 kafka容器: docker run containerId三:开放防火墙 一:拉取 kafka镜像: docker pull bitnami/kafka docker search kafka docke…

Android Framework通信:Handler

文章目录 前言一、Handler源码分析1、创建Handler2、发送消息3、取消息4、消息处理5、线程切换的方法(Handler异步消息处理机制流程)handler.sendMessage()handler.post()View.post()Activity中的runOnUiThread() 二、Handler高频面试题1、为什么要有Han…

SSM - Springboot - MyBatis-Plus 全栈体系(二十八)

第六章 SpringBoot 三、SpringBoot3 整合 SpringMVC 1. 实现过程 1.1 创建程序 1.2 引入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001…

单目3D目标检测——MonoDLE 模型训练 | 模型推理

本文分享 MonoDLE 的模型训练、模型推理、可视化3D检测结果。 模型原理&#xff0c;参考我这篇博客&#xff1a;【论文解读】单目3D目标检测 MonoDLE&#xff08;CVPR2021&#xff09;_一颗小树x的博客-CSDN博客 源码地址&#xff1a;https://github.com/xinzhuma/monodle 目…

2. 验证1101序列(Mealy)

题目要求&#xff1a; 用 M e a l y \rm Mealy Mealy型状态机验证 1101 1101 1101序列 题目描述&#xff1a; 使用状态机验证 1101 1101 1101序列&#xff0c;注意&#xff1a;允许重复子序列。 方法一&#xff1a; 去掉 M o o r e \rm Moore Moore的 s 4 s_4 s4​&#xff…

【LeetCode热题100】--136.只出现一次的数字

136.只出现一次的数字 使用哈希表&#xff1a; class Solution {public int singleNumber(int[] nums) {Map<Integer,Integer> map new HashMap<>();for(int num:nums){Integer count map.get(num);if(count null){count 1;}else{count;}map.put(num,count);}…

打造个人专属形象!工业级人物写真生成工具FaceChain开源

简介 FaceChain 是一个可以用来打造个人数字形象的深度学习模型工具。用户仅需要提供最低一张照片即可获得独属于自己的个人形象数字替身。FaceChain 支持在 gradio 的界面中使用模型训练和推理能力&#xff0c;也支持资深开发者使用 python 脚本进行训练推理。 Github链接&…