数据结构与算法Python版 图的应用与广度优先搜索

news2025/1/6 8:06:03

文章目录

  • 一、图的应用-词梯问题
  • 二、图的广度优先搜索


一、图的应用-词梯问题

词梯问题 Word Ladder

  • 从一个单词演变到另一个单词,其中的过程可以经过多个中间单词。要求是相邻两个单词之间差异只能是1个字母
  • 如FOOL变SAGE:FOOL >> POOL >> POLL >> POLE >> PALE >>SALE >> SAGE
  • 采用图来解决这个问题,我们的目标是找到最短的单词变换序列
    • 将可能的单词之间的演变关系表达为图
    • 采用“广度优先搜索 BFS”,来搜寻从开始单词到结束单词之间的所有有效路径
    • 选择其中最快到达目标单词的路径

构建单词关系图

  • 将单词作为顶点的标识Key,如果两个单词之间仅相差1个字母,就在它们之间设一条边
  • 使用无向图,边没有权重
  • 算法一:将所有单词作为顶点加入图中,对每个顶点(单词),与其它所有单词进行比较,如果相差仅1个字母,则建立一条边。时间复杂度是O(n^2)
  • 算法二:将所有单词作为顶点加入图中,对于有N个字母的单词,建立N个桶,每个桶可以存放若干单词。单词的其中一个字母使用_通配符,所有匹配的单词放到同一桶中。最后,在同一个桶的单词之间建立边。

在这里插入图片描述

单词文件words.txt的内容如下:

fool
cool
pool
poll
pole
pall
fall
fail
foil
foul
pale
page
sage
pope
sale

构建单词关系图

def build_graph(word_file):
    """构建单词关系图"""
    buckets = {}
    g = Graph()
    path = Path(word_file)
    contents = path.read_text()
    words = contents.splitlines()
    # 建立桶
    for word in words:
        for i in range(len(word)):
            bucket_key = word[:i] + "_" + word[i + 1 :]
            # 如果key存在,则把单词加入到桶的列表;否则,创建桶和桶列表的第一个单词
            if bucket_key in buckets:
                buckets[bucket_key].append(word)
            else:
                buckets[bucket_key] = [word]

    # 建立边
    for bucket_key in buckets.keys():
        for word1 in buckets[bucket_key]:
            for word2 in buckets[bucket_key]:
                if word1 != word2:
                    g.add_edge(word1, word2)

    return g


word_file = "words.txt"
word_graph = build_graph(word_file)
for values in word_graph.vertexes.values():
    print(values)
    
    
### 输出结果
fool connected to : ['cool', 'pool', 'foil', 'foul']
cool connected to : ['fool', 'pool']
pool connected to : ['fool', 'cool', 'poll']        
foil connected to : ['fool', 'foul', 'fail']        
foul connected to : ['fool', 'foil']
poll connected to : ['pool', 'pall', 'pole']        
pall connected to : ['poll', 'fall', 'pale']        
pole connected to : ['poll', 'pale', 'pope']        
pale connected to : ['pole', 'pall', 'sale', 'page']
pope connected to : ['pole']
fall connected to : ['pall', 'fail']
fail connected to : ['fall', 'foil']
sale connected to : ['pale', 'sage']
page connected to : ['pale', 'sage']
sage connected to : ['page', 'sale']

二、图的广度优先搜索

广度优先搜索 Breadth First Search

  • 给定图G,以及开始搜索的起始顶点s,搜索所有从顶点s可到达顶点的边。在达到更远的距离k+1的顶点之前,找到全部距离为k的顶点
  • 就像构建一棵树的过程,从顶部向下逐步增加层次。在增加层次之前,添加了所有兄弟节点到树中

广度优先搜索-算法过程

  • 为了跟踪顶点的加入过程,并避免重复顶点,为顶点增加3个属性

    • 距离distance:从起始顶点到此顶点路径长度;
    • 前驱顶点predecessor:可反向追溯到起点;
    • 颜色color:标识了此顶点是尚未发现(白色)、已经发现(灰色)、还是已经完成探索(黑色)
  • 为决定下一个要探索的顶点,用一个队列Queue来对已发现的顶点进行排列

  • 从起始顶点s开始,作为刚发现的顶点,标注为灰色,距离为0,前驱为None,加入队列,接下来是个循环迭代过程:

    • 从队首取出一个顶点作为当前顶点;
    • 遍历当前顶点的邻接顶点,如果是尚未发现的白色顶点,则将其颜色改为灰色(已发现),距离增加1,前驱顶点为当前顶点,加入到队列中
    • 遍历完成后,将当前顶点设置为黑色(已探索过),循环回到步骤1的队首取当前顶点

更新后的顶点类和图类

class Vertex:
    """顶点类"""

    def __init__(self, key, distance=0, pred=None, color="white"):
        self.key = key
        self.connected_to = {}
        self.distance = distance
        self.pred = pred
        self.color = color

    def __str__(self):
        return f"{self.key} connected to : {[x.key for x in self.connected_to]}"

    def add_neighbor(self, nbr, weight=0):
        """键为nbr顶点对象,值为权重"""
        self.connected_to[nbr] = weight

    def get_connections(self):
        return self.connected_to.keys()

    def get_key(self):
        return self.key

    def get_weight(self, nbr):
        return self.connected_to.get(nbr, None)

    def set_distance(self, distance):
        self.distance = distance

    def get_distance(self):
        return self.distance

    def set_pred(self, pred):
        self.pred = pred

    def get_pred(self):
        return self.pred

    def set_color(self, color):
        self.color = color

    def get_color(self):
        return self.color


class Graph:
    """图"""

    def __init__(self):
        self.vertexes = {}
        self.num_vertexes = 0

    def __contains__(self, key):
        return key in self.vertexes

    def __iter__(self):
        return iter(self.vertexes.values())

    def add_vertex(self, key):
        """加入顶点:以key为键,以Vertex对象为值"""
        self.num_vertexes += 1
        new_vertex = Vertex(key)
        self.vertexes[key] = new_vertex
        return new_vertex

    def get_vertex(self, key):
        if key in self.vertexes:
            return self.vertexes[key]
        else:
            return None

    def add_edge(self, begin, end, cost=0):
        """添加边,如果顶点不存在,先添加顶点再加边"""
        if begin not in self.vertexes:
            self.add_vertex(begin)
        if end not in self.vertexes:
            self.add_vertex(end)
        self.vertexes[begin].add_neighbor(self.vertexes[end], cost)

    def get_vertexes(self):
        """返回所有顶点"""
        return self.vertexes.keys()

    def get_edge_count(self):
        count = 0
        for i in self.vertexes.values():
            count += len(i.connected_to)
        return count

广度优先搜索-算法实现

from pathlib import Path
from my_queue import Queue

def bfs(graph: Graph, start_word):
    """广度优先搜索:以start_word为起点,遍历其它顶点,并为每个顶点着色、赋距离和前驱"""
    start = graph.get_vertex(start_word)
    start.set_distance(0)
    start.set_pred(None)
    v_queue = Queue()
    v_queue.enqueue(start)
    while v_queue.size() > 0:
        current_vert: Vertex = v_queue.dequeue()
        for nbr in current_vert.get_connections():
            if nbr.get_color() == "white":
                nbr.set_color("gray")
                nbr.set_distance(current_vert.get_distance() + 1)
                nbr.set_pred(current_vert)
                v_queue.enqueue(nbr)
        current_vert.set_color("black")

词梯问题求解

def traverse(graph: Graph, target_word):
    """输出目标单词的路径,即词梯问题的解"""
    target = graph.get_vertex(target_word)
    x = target
    while x.get_pred() != None:
        print(x.get_key())
        x = x.get_pred()
    print(x.get_key())


word_file = "words.txt"
word_graph = build_graph(word_file)
bfs(word_graph, "fool")
traverse(word_graph, "sage")


### 输出结果
sage
sale
pale
pall
poll
pool
fool

广度优先搜索BFS- 算法分析

  • 广度优先搜索算法主体是两个循环的嵌套。while循环对每个顶点访问一次,所以是O(|V|);嵌套在while中的for,由于每条边只有在其起始顶点u出队的时候才会被检查一次,所以边最多被检查1次,一共是O(|E|)。综合起来BFS的时间复杂度为O(|V|+|E|)
  • 建立BFS树之后,回溯顶点到起始顶点的过程最差情况为O(|V|);创建单词关系图最差情况为O(|V|2)。

您正在阅读的是《数据结构与算法Python版》专栏!关注不迷路~

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

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

相关文章

服务器数据恢复—服务器硬盘亮黄灯的数据恢复案例

服务器硬盘指示灯闪烁黄灯是一种警示,意味着服务器硬盘出现故障即将下线。发现这种情况建议及时更换硬盘。 一旦服务器上有大量数据频繁读写,硬盘指示灯会快速闪烁。服务器上某个硬盘的指示灯只有黄灯亮着,而其他颜色的灯没有亮的话&#xff…

Java SpringBoot使用EasyExcel导入导出Excel文件

点击下载《Java SpringBoot使用EasyExcel导入导出Excel文件(源代码)》 在 Java Spring Boot 项目中,导入(读取)和导出(写入) Excel 文件是一项常见的需求。EasyExcel 是阿里巴巴开源的一个用于简化 Java 环境下 Excel…

Deduction(演绎法)和Reduction(还原法)-关于中西方思维的差异

Deduction(演绎法)和Reduction(还原法)-关于中西方思维的差异 最近看到中国新一代战机上天的消息,感慨万千;忽然想起来两年多前一次爬山的时候,一个友人跟我大概说过,Deduction和Reduction分别对应了中国古代和西方古代以来的思考自然和技术发明的思想.于是又在这方面琢磨了一番…

unity学习6:unity的3D项目的基本操作

目录 1 unity界面的基本认识 1.1 file 文件 1.2 edit 编辑/操作 1.3 Assets 1.4 gameobject 游戏对象 1.5 组件 1.6 windows 2 这些部分之间的关系 2.1 关联1: Assets & Project 2.2 关联2:gameobject & component 2.3 关联3&#xff…

【银河麒麟高级服务器操作系统实例】tcp半链接数溢出分析及处理全过程

了解更多银河麒麟操作系统全新产品,请点击访问 麒麟软件产品专区:https://product.kylinos.cn 开发者专区:https://developer.kylinos.cn 文档中心:https://document.kylinos.cn 服务器环境以及配置 系统环境 物理机/虚拟机/云…

k8s基础(2)—Kubernetes-Namespace

一、Namespace概述 名字空间 在 Kubernetes 中,名字空间(Namespace) 提供一种机制,将同一集群中的资源划分为相互隔离的组。 同一名字空间内的资源名称要唯一,但跨名字空间时没有这个要求。 名字空间作用域仅针对带有…

.NET框架用C#实现PDF转HTML

HTML作为一种开放标准的网页标记语言,具有跨平台、易于浏览和搜索引擎友好的特性,使得内容能够在多种设备上轻松访问并优化了在线分享与互动。通过将PDF文件转换为HTML格式,我们可以更方便地在浏览器中展示PDF文档内容,同时也更容…

医学图像分析工具01:FreeSurfer || Recon -all 全流程MRI皮质表面重建

FreeSurfer是什么 FreeSurfer 是一个功能强大的神经影像学分析软件包,广泛用于处理和可视化大脑的横断面和纵向研究数据。该软件由马萨诸塞州总医院的Martinos生物医学成像中心的计算神经影像实验室开发,旨在为神经科学研究人员提供一个高效、精确的数据…

JavaScript 基础2

js的运算符 算数运算符 相加求和&#xff0c;如果用在字符串则是拼接 -相减求差 *相乘求积 /相除求商 %模除求余 具体用法如下 let num 154 let num2 15 document.write(numnum2) document.write(<br>) document.write(num-num2) document.write(<br>) do…

Leecode刷题C语言之我的日程安排表②

执行结果:通过 执行用时和内存消耗如下&#xff1a; typedef struct {int start;int end; }BOOKING;#define MAX_BOOK_NUM (1000) typedef struct MyCalendar_ {BOOKING book[MAX_BOOK_NUM];int bnum;BOOKING *sorted[MAX_BOOK_NUM];int num;int conflict[MAX_BOOK_NUM];int c…

【C语言的小角落】--- 深度理解取余/取模运算

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; C语言的小角落 本篇博客我们来深度理解取余/取模&#xff0c;以及它们在不同语言中出现不同现象的原因。 &#x1f3e0; 关于取整 &#x1f3b5; 向0取整…

网关的主要类型和它们的特点

网关&#xff0c;作为网络通信的关键节点&#xff0c;根据其应用场景和功能特点&#xff0c;可以分为多种类型。 1.协议网关 特点&#xff1a; • 协议转换&#xff1a;协议网关的核心功能是转换不同网络之间的通信协议。例如&#xff0c;它可以将IPv4协议的数据包转换为IPv6协…

聆听音乐 1.5.9 | 畅听全网音乐,支持无损音质下载

聆听音乐手机版是面向广大音乐爱好者的移动应用程序&#xff0c;用户可以随时随地通过手机享受丰富的音乐资源。它提供了多种魅力功能&#xff0c;让用户在手机上畅享更舒适的音乐体验&#xff0c;每位用户都能享受精彩纷呈的收听体验。此外&#xff0c;软件还支持无损音质音乐…

实现一个通用的树形结构构建工具

文章目录 1. 前言2. 树结构3. 具体实现逻辑3.1 TreeNode3.2 TreeUtils3.3 例子 4. 小结 1. 前言 树结构的生成在项目中应该都比较常见&#xff0c;比如部门结构树的生成&#xff0c;目录结构树的生成&#xff0c;但是大家有没有想过&#xff0c;如果在一个项目中有多个树结构&…

“善弈者”也需妙手,Oclean欧可林:差异化不是说说而已

作者 | 曾响铃 文 | 响铃说 俗话说&#xff0c;“牙痛不是病&#xff0c;痛起来要人命”。这话意思大家都知道&#xff0c;牙痛虽不是什么大病&#xff0c;可一旦发作却是极难忍受。 前几日&#xff0c;Oclean欧可林举办了一场AirPump A10氧气啵啵冲牙器新品品鉴会&#xff…

汇编语言与接口技术--跑马灯

一、 实验要求 在单片机开发板的LED灯D1~D8上实现跑马灯。LED与单片机引脚连线电路如下图: 单片机芯片选择AT89C51&#xff0c;晶振频率设为12MHz&#xff0c;操作参考单片机开发板使用说明。跑马灯点亮的时间间隔约为1秒。分别用定时器的模式1和模式2实现。&#xff08;用P83…

基于数据融合的智能家居环境监测系统研究与设计(论文+源码)

1总体方案设计 本次基于数据融合的智能家居环境监测系统的设计&#xff0c;其系统总体架构如图2.1所示&#xff0c;整个系统在器件上包括了主控制器STM32F103单片机&#xff0c;MQ可燃气体传感器&#xff0c;光照传感器&#xff0c;DHT11温湿度传感器&#xff0c;风扇&#xff…

Mac中配置Node.js前端vscode环境(第二期)

核心组件&#xff1a;vscode、谷歌浏览器、Node.js&#xff08;重点&#xff09;、git 一、Node.js安装&#xff08;nvm安装&#xff09; 点击macos中的终端&#xff0c;保持bash&#xff0c;而不是zsh 若为zsh&#xff0c;则可在终端中使用下面命令变成bash chsh -s /bin/…

基于SpringBoot和Thymeleaf的仿小米电商系统源码下载与安装指南-幽络源

项目概述 这是一个基于 Spring Boot 2.X 和 Thymeleaf 技术栈的仿小米电商系统。该项目包括了前台商城系统和后台管理系统&#xff0c;经幽络源测试具备完整的电商功能&#xff0c;适合用作学习、参考或作为开发电商系统的基础。 前台商城系统&#xff1a; 包括首页登录、商品…

Deepseek v3 的笔记

基本概述 Deepseek v3是Deepseek发布的旗舰模型&#xff0c;属于607B的混合专家&#xff08;MoE&#xff09;模型&#xff0c;其中活跃参数为37B。在当前的模型领域&#xff0c;它堪称最佳的开源模型&#xff0c;性能超越了Llama 3.1 405b、Qwen和Mistral等知名模型。根据基准…