【17】数据结构之图的遍历篇章

目录标题

    • 图的遍历
      • 深度优先遍历 Depth First Search
      • 广度优先遍历 Breadth First Search

图的遍历

从图中某一个顶点出发,沿着一些边访遍图中所有的顶点,且使用每个顶点仅被访问一次,这个过程称为图的遍历.Graph Traversal.

  • 其中,访问限制标志visited[n],只取0和1值.
  • 遍历算法:
    • 访问初始顶点,
    • 按照某种策略依次访问连通子图中未被访问的顶点,
    • 寻找下一个未被访问的顶点,将此顶点作为初始顶点,转上一步骤,
    • 重复上述步骤直到所有顶点均被访问为止.

深度优先遍历 Depth First Search

树先序遍历的推广.

  • 基本思想:

  • 1-先任意选定图中一个顶点v,从顶点v开始访问:再选定v的一个没有被访问过的邻接点w,对顶点w进行深度优先遍历,直到图中与当前顶点邻接的顶点全部被访问为止.

  • 2-如果仍然有顶点未被访问,则从未访问的顶点中任选一个,执行前述遍历过程.

  • 总言:是尽可能深地探索分支,当遇到末端(叶子节点或无法继续前进的点)时回溯,转而探索其他未被访问的分支.

  • 最好操作是这么进行的访问过程是通过栈来控制.

  • 栈的进出规则:

    • 访问的顶点入栈,继续寻找邻接顶点,找到入栈;
    • 直到当前的顶点找不到邻接顶点,当前栈顶出栈;
    • 访问当前栈顶的元素是否还存在未被访问的邻接顶点,没有就出栈;
    • 有的话就访问当前栈顶元素未被访问的邻接顶点;
    • 没有的话,直到栈为空,表明图的遍历完成.
    • 示意图
      在这里插入图片描述
  • 代码实现

# 深度优先遍历
class Stack:"""栈数据结构实现"""def __init__(self):self.items = []def is_empty(self):"""判断栈是否为空"""return len(self.items) == 0def push(self, item):"""元素入栈"""self.items.append(item)def pop(self):"""元素出栈并返回"""if not self.is_empty():return self.items.pop()return Nonedef peek(self):"""查看栈顶元素"""if not self.is_empty():return self.items[-1]return Noneclass Vertex:"""顶点类"""def __init__(self, data):self.data = data  # 顶点数据self.first_edge = None  # 指向第一条邻接边class Edge:"""边类"""def __init__(self, adj_vex):self.adj_vex = adj_vex  # 邻接顶点索引self.next_edge = None  # 指向下一条边class LinkedGraph:"""邻接表图结构"""def __init__(self, vertexs, edges):self.vertex_num = len(vertexs)self.edge_num = len(edges)self.vertex_list = [None] * self.vertex_num  # 顶点列表# 初始化顶点和边self.add_vertex(vertexs)self.add_edge(edges)def add_vertex(self, vertexs):"""添加顶点集合"""for i in range(self.vertex_num):self.vertex_list[i] = Vertex(vertexs[i])def add_edge(self, edges):"""添加无向边集合"""for edge in edges:# 获取顶点在列表中的索引v1_idx = self.get_vertex_index(edge[0])v2_idx = self.get_vertex_index(edge[1])if v1_idx == -1 or v2_idx == -1:raise ValueError("顶点不存在")# 为两个顶点添加边(无向图双向添加)self._add_single_edge(v1_idx, v2_idx)self._add_single_edge(v2_idx, v1_idx)def _add_single_edge(self, from_idx, to_idx):"""添加单条边到指定顶点"""new_edge = Edge(to_idx)# 头插法提高效率if self.vertex_list[from_idx].first_edge is None:self.vertex_list[from_idx].first_edge = new_edgeelse:new_edge.next_edge = self.vertex_list[from_idx].first_edgeself.vertex_list[from_idx].first_edge = new_edgedef get_vertex_index(self, data):"""根据顶点值查找索引"""for i in range(self.vertex_num):if self.vertex_list[i].data == data:return ireturn -1class DepthFirstSearch:"""深度优先遍历实现"""def __init__(self, graph):self.graph = graphself.visited = [False] * graph.vertex_num  # 访问标记数组self._dfs_all()  # 执行遍历def _dfs_all(self):"""遍历所有连通分量"""for i in range(self.graph.vertex_num):if not self.visited[i]:self._dfs_iterative(i)def _dfs_iterative(self, start_idx):"""迭代式DFS实现"""stack = Stack()# 访问起始顶点self.visited[start_idx] = Trueprint(self.graph.vertex_list[start_idx].data, end=" ")stack.push(start_idx)while not stack.is_empty():# 获取当前顶点未访问的邻接点current_idx = stack.peek()edge = self.graph.vertex_list[current_idx].first_edgefound = Falsewhile edge:adj_idx = edge.adj_vexif not self.visited[adj_idx]:# 访问并标记邻接点self.visited[adj_idx] = Trueprint(self.graph.vertex_list[adj_idx].data, end=" ")stack.push(adj_idx)found = Truebreakedge = edge.next_edge# 没有未访问邻接点时回溯if not found:stack.pop()# ---------------------- 测试用例 ----------------------
if __name__ == "__main__":# 测试数据(无向图)vertexs = [1, 2, 3, 4, 5, 6]edges = [[1, 2], [2, 3], [3, 4], [4, 5],[2, 4], [1, 5], [2, 5], [5, 6]]# 创建图对象graph = LinkedGraph(vertexs, edges)# 执行深度优先遍历print("DFS遍历结果:", end=" ")dfs = DepthFirstSearch(graph)# 正确输出应为:1 2 3 4 5 6 或类似连通路径1 5 6 2 4 3

广度优先遍历 Breadth First Search

  • 核心思想:任意选定一个顶点v开始本次访问,在访问过v后依次访问v的待访问邻接点,并将已访问的顶点放入队列 Q中,按照Q中顶点的次序,依次访问这些已被访问过的顶点的邻接点,如果队首的顶点不存在待访问邻接点,让队首顶点出队,访问新队首的待访问邻接点,如此进行下去直至队列为空。

  • 主要就是对当前顶点的所有邻接点进行访问,然后下一个,再访问其所有的邻接点,一直重复。

  • 直到所有顶点均被访问过。

  • 示意图
    在这里插入图片描述

  • 代码实现

# 广度优先遍历
from collections import deque  # 使用高效双端队列class Vertex:"""顶点类"""def __init__(self, data):self.data = data  # 顶点数据self.first_edge = None  # 指向第一条邻接边class Edge:"""边类"""def __init__(self, adj_vex):self.adj_vex = adj_vex  # 邻接顶点索引self.next_edge = None  # 指向下一条边class LinkedGraph:"""邻接表图结构"""def __init__(self, vertices, edges):self.vertex_num = len(vertices)self.edge_num = 0self.vertex_list = [None] * self.vertex_num# 初始化顶点和边self._add_vertices(vertices)self._add_edges(edges)def _add_vertices(self, vertices):"""添加顶点集合"""for i in range(self.vertex_num):self.vertex_list[i] = Vertex(vertices[i])def _add_edges(self, edges):"""添加无向边集合"""for v1, v2 in edges:idx1 = self._get_vertex_index(v1)idx2 = self._get_vertex_index(v2)if idx1 == -1 or idx2 == -1:raise ValueError("顶点不存在")# 双向添加边(无向图)self._add_single_edge(idx1, idx2)self._add_single_edge(idx2, idx1)self.edge_num += 2def _add_single_edge(self, from_idx, to_idx):"""头插法添加单条边"""new_edge = Edge(to_idx)new_edge.next_edge = self.vertex_list[from_idx].first_edgeself.vertex_list[from_idx].first_edge = new_edgedef _get_vertex_index(self, data):"""根据顶点值查找索引"""for i in range(self.vertex_num):if self.vertex_list[i].data == data:return ireturn -1class BreadthFirstSearch:"""广度优先遍历实现"""def __init__(self, graph):self.graph = graphself.visited = [False] * graph.vertex_numself._bfs_all()def _bfs_all(self):"""遍历所有连通分量"""for i in range(self.graph.vertex_num):if not self.visited[i]:self._bfs(i)def _bfs(self, start_idx):"""单连通分量BFS"""queue = deque()# 访问起始顶点self.visited[start_idx] = Trueprint(self.graph.vertex_list[start_idx].data, end=" ")queue.append(start_idx)while queue:current_idx = queue.popleft()edge = self.graph.vertex_list[current_idx].first_edgewhile edge:neighbor_idx = edge.adj_vexif not self.visited[neighbor_idx]:self.visited[neighbor_idx] = Trueprint(self.graph.vertex_list[neighbor_idx].data, end=" ")queue.append(neighbor_idx)edge = edge.next_edge# ---------------------- 测试用例 ----------------------
if __name__ == "__main__":vertices = [1, 2, 3, 4, 5, 6]edges = [(1, 2), (2, 3), (3, 4), (4, 5),(2, 4), (1, 5), (2, 5), (5, 6)]# 创建图对象graph = LinkedGraph(vertices, edges)# 执行广度优先遍历print("BFS遍历结果:", end=" ")bfs = BreadthFirstSearch(graph)

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

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

相关文章

简单接口工具(ApiCraft-Web)

ApiCraft-Web 项目介绍 ApiCraft-Web 是一个轻量级的 API 测试工具,提供了简洁直观的界面,帮助开发者快速测试和调试 HTTP 接口。 功能特点 支持多种 HTTP 请求方法(GET、POST、PUT、DELETE)可配置请求参数(Query …

Git进阶操作

Git高阶操作完全指南:解锁专业开发工作流 前言 在当今的软件开发领域,掌握高级Git技能已成为区分普通开发者与专业开发者的关键因素。根据最新的GitHub数据,熟练应用交互式暂存和Rebase等高级功能的开发者,其代码审查通过率平均提…

Python结合AI生成图像艺术作品代码及介绍

为实现生成图像艺术作品,我选用 Stable Diffusion 库结合 Python 编写代码。下面先展示代码,再详细介绍其原理、模块及使用方法等内容。 生成图片代码 import torch from diffusers import StableDiffusionPipeline# 加载预训练模型 pipe StableDiffu…

Linux操作系统--静态库和动态库的生成and四种解决加载找不到动态库的四种方法

目录 必要的知识储备: 生成静态库: 生成动态库: 解决加载找不到动态库的四种方法: 第一种:拷贝到系统默认的库路径 /usr/lib64/ 第二种:在系统默认的库路径/usr/lib64/下建立软链接 第三种&#xff1…

LLM中的N-Gram、TF-IDF和Word embedding

文章目录 1. N-Gram和TF-IDF:通俗易懂的解析1.1 N-Gram:让AI学会"猜词"的技术1.1.1 基本概念1.1.2 工作原理1.1.3 常见类型1.1.4 应用场景1.1.5 优缺点 1.2 TF-IDF:衡量词语重要性的尺子1.2.1 基本概念1.2.2 计算公式1.2.3 为什么需…

Leetcode 3359. 查找最大元素不超过 K 的有序子矩阵【Plus题】

1.题目基本信息 1.1.题目描述 给定一个大小为 m x n 的二维矩阵 grid。同时给定一个 非负整数 k。 返回满足下列条件的 grid 的子矩阵数量: 子矩阵中最大的元素 小于等于 k。 子矩阵的每一行都以 非递增 顺序排序。 矩阵的子矩阵 (x1, y1, x2, y2) 是通过选择…

如何在 Ubuntu 22.04 上安装、配置、使用 Nginx

如何在 Ubuntu 22.04 上安装、配置、使用 Nginx?-阿里云开发者社区 更新应用 sudo apt updatesudo apt upgrade检查必要依赖并安装 sudo apt install -y curl gnupg2 ca-certificates lsb-release安装nginx sudo apt install -y nginx# 启动nginx sudo systemct…

Linux:显示 -bash-4.2$ 问题(CentOS 7)

文章目录 一、原因二、错误示例三、解决办法 一、原因 在 CentOS 7 系统中,如果你看到命令行提示符显示为 -bash-4.2$,一般是 Bash shell 正在运行,并且它没有找到用户的个人配置文件,或者这些文件有问题而未能成功加载。这个提示…

QT6 源(34):随机数生成器类 QRandomGenerator 的源码阅读

&#xff08;1&#xff09;代码来自 qrandom.h &#xff0c;结合官方的注释&#xff1a; #ifndef QRANDOM_H #define QRANDOM_H#include <QtCore/qalgorithms.h> #include <algorithm> // for std::generate #include <random> // for std::mt1993…

第二篇:linux之Xshell使用及相关linux操作

第二篇&#xff1a;linux之Xshell使用及相关linux操作 文章目录 第二篇&#xff1a;linux之Xshell使用及相关linux操作一、Xshell使用1、Xshell安装2、Xshell使用 二、Bash Shell介绍与使用1、什么是Bash Shell(壳)&#xff1f;2、Bash Shell能干什么&#xff1f;3、平时如何使…

MCP(模型上下文协议)学习笔记

学习MCP&#xff08;模型上下文协议&#xff09;的系统化路径&#xff0c;结合技术原理、工具实践和社区资源&#xff0c;帮助你高效掌握这一AI交互标准&#xff1a; 在当今人工智能飞速发展的时代&#xff0c;AI技术正以前所未有的速度改变着我们的生活和工作方式。然而&#…

MIR-2025 | 多模态知识助力机器人导航:从复杂环境到高效路径规划

作者&#xff1a;Hui Yuan, Yan Huang, Zetao Du, Naigong Yu, Ziqi Liu, Dongbo Zhang, Kun Zhang 单位&#xff1a;北京工业大学信息科学与技术学院&#xff0c;北京工业大学计算智能与智能系统北京市重点实验室&#xff0c;中科院自动化研究所模式识别国家重点实验室与多智…

javaSE.泛型界限

现在有一个新的需求&#xff0c;没有String类型成绩了&#xff0c;但是成绩依然可能是整数&#xff0c;也可能是小数&#xff0c;这是我们不希望用户将泛型指定为除数字类型外的其他类型&#xff0c;我们就需要使用到泛型的上界定义&#xff1a; 上界&#x1f447;只能使用其本…

压缩包网页预览(zip-html-preview)

zip-html-preview 项目介绍 这是一个基于 Spring Boot 开发的在线 ZIP 文件预览工具,主要用于预览 ZIP 压缩包中的 HTML 文件及其相关资源。 主要功能 支持拖拽上传或点击选择多个 ZIP 文件自动解压并提取 ZIP 文件中的 HTML 文件在线预览 HTML 文件及其相关的 CSS、JavaSc…

QML之Overlay

Overlay&#xff08;覆盖层&#xff09;是QML中用于在当前界面之上显示临时内容的重要组件。 一、Overlay基础概念 1.1 什么是Overlay&#xff1f; Overlay是一种浮动在现有界面之上的视觉元素&#xff0c;具有以下特点&#xff1a; 临时显示&#xff0c;不影响底层布局 通…

iso17025证书申请方法?iso17025认证意义

ISO/IEC 17025证书申请方法 ISO/IEC 17025是检测和校准实验室能力的国际标准&#xff0c;申请CNAS认可的流程如下&#xff1a; 1. 前期准备 标准学习&#xff1a;深入理解ISO/IEC 17025:2017标准要求。 差距分析&#xff1a;评估现有实验室管理与技术能力与标准的差距。 制…

reverse3 1(Base加密)

题目 做法 下载安装包&#xff0c;解压&#xff0c;把解压后的文件拖进Exeinfo PE进行分析 32位&#xff0c;无壳 扔进IDA&#xff08;32位&#xff09;&#xff0c;找到main&#xff0c;F5反编译 只是因为在人群中多看了你一眼——第31行的right flag&#xff0c;关键词找到…

电控---CMSIS概览

1. CMSIS库简介 CMSIS&#xff08;Cortex Microcontroller Software Interface Standard&#xff0c;Cortex微控制器软件接口标准&#xff09;是由ARM公司开发的一套标准化软件接口&#xff0c;旨在为基于ARM Cortex-M系列处理器&#xff08;如Cortex-M0/M0/M3/M4/M7/M33等&am…

list.

列表类型是用来存储多个有序的字符串&#xff0c;列表中的每个字符串称为元素&#xff08;element&#xff09;&#xff0c;⼀个列表最多可以存储个元素 在 Redis 中&#xff0c;可以对列表两端插入&#xff08;push&#xff09;和弹出&#xff08;pop&#xff09;&#xff0c;…

关于Diamond机械手的运动学与动力学的推导

1.关于Diamond机械手 &#xff08;1&#xff09;位置模型推导 逆解&#xff1a;机械末端平台的位置与驱动关节之间的关系。 设p点在xy平面的坐标是&#xff08;x&#xff0c;y&#xff09;T&#xff0c;此时根据向量求解 OP等于向量r等于e向xy轴的向量主动臂长度向xy轴的向量…