Python - 深夜数据结构与算法之 Two-Ended BFS

这篇具有很好参考价值的文章主要介绍了Python - 深夜数据结构与算法之 Two-Ended BFS。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

目录

一.引言

二.双向 BFS 简介

1.双向遍历示例

2.搜索模版回顾

三.经典算法实战

1.Word-Ladder [127]

2.Min-Gen-Mutation [433]

四.总结


一.引言

DFS、BFS 是常见的初级搜索方式,为了提高搜索效率,衍生了剪枝、双向 BFS 以及 A* 即启发式搜索等高级搜索方式。剪枝通过避免不必要或者次优解来减少搜索的次数,提高搜索效率;双向 BFS 通过层序遍历从首尾逼近答案,提高搜索效率;启发式搜索则是从优先级的角度出发,基于优先级高低搜索,提高搜索效率。本文主要介绍双向 BFS 的使用。

二.双向 BFS 简介

1.双向遍历示例

◆  双向连通图

求 A -> L 所需最短路径。

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

◆  遍历层级关系

不同颜色代表不同层级的 BFS,绿色为 root,蓝色为第二层,从左向右递推。

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

◆  双向遍历

从 A/L 同时层序遍历,当二者扩散的点重合时,左右路径长度相加即为最短路径。

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

2.搜索模版回顾

◆ DFS - 递归

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

◆ DFS - 非递归 

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

◆ BFS - 栈 

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

三.经典算法实战

1.Word-Ladder [127]

单词接龙: https://leetcode.cn/problems/word-ladder/description/

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

◆ 单向 BFS

class Solution:    
    def ladderLength(self, beginWord, endWord, wordList):
        """
        :type beginWord: str
        :type endWord: str
        :type wordList: List[str]
        :rtype: int
        """
        valid_word = set(wordList)

        if endWord not in valid_word:
            return 0

        stack = [(beginWord, 1)]

        while stack:
            word, level = stack.pop(0)

            for i in range(len(word)):
                for char in "abcdefghijklmnopqrstuvwxyz":
                    new_word = word[:i] + char + word[i + 1:]

                    if new_word == endWord:
                        return level + 1
                    elif new_word in valid_word:
                        stack.append((new_word, level + 1))
                        valid_word.remove(new_word)

        return 0

这里我们可以打印一下转换的流程图,hot 有多层 level 出发,第二条路径走到了 cog,即结束遍历,当然 log 也可以走到 cog 只不过已经不需要了。

hot 2 -> lot 3

hot 2 -> dot 3 -> dog 4 -> cog 5

hot 2 -> dot 3 -> log 4 

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

◆ 双向 BFS 

class Solution(object):
    def ladderLength(self, beginWord, endWord, wordList):
        """
        :type beginWord: str
        :type endWord: str
        :type wordList: List[str]
        :rtype: int
        """
        # 去重使用
        valid_word = set(wordList)

        # 边界条件
        if endWord not in wordList or len(wordList) == 0:
            return 0

        # 双向 BFS
        begin, end, step = {beginWord}, {endWord}, 1


        # 同时有元素才能继续,如果一遍没元素代表已中断,无法联通,直接结束
        while begin and end:

            # 减少排查的可能性,从单词少的方向排查,避免无效查询
            if len(begin) > len(end):
                begin, end = end, begin

            # 存储下一层
            next_level = set()
            # 遍历下一层的多个结果
            for word in begin:
                # 遍历每个位置
                for i in range(len(word)):
                    # a-z
                    for char in "abcdefghijklmnopqrstuvwxyz":
                        # 节省无必要的替换
                        if char != word[i]:
                            new_word = word[:i] + char + word[i + 1:]
                            # 二者相遇即返回
                            if new_word in end:
                                return step + 1
                            if new_word in valid_word:
                                next_level.add(new_word)
                                valid_word.remove(new_word)

            # 指针替换
            begin = next_level
            step += 1

        return 0

已经将详细的注释加在代码里了,从 {start},{end} 两个方向查找,每次只找短的缩小无效查询的次数,这其实也是一种剪枝的策略,正所谓图中有真意欲辨已忘言:

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

◆ 双向 BFS + 剪枝

class Solution(object):
    def ladderLength(self, beginWord, endWord, wordList):
        """
        :type beginWord: str
        :type endWord: str
        :type wordList: List[str]
        :rtype: int
        """
        # 去重使用
        valid_word = set(wordList)

        if endWord not in wordList or len(wordList) == 0:
            return 0

        # 剪枝优化
        s = set()
        for word in wordList:
            for char in word:
                s.add(char)

        s = ''.join(list(s))

        # 双向 BFS
        begin, end, step = {beginWord}, {endWord}, 1

        while begin and end:

            if len(begin) > len(end):
                begin, end = end, begin

            # 存储下一层
            next_level = set()
            for word in begin:
                for i in range(len(word)):
                    # a-z
                    for char in s:
                        # 节省无必要的替换
                        if char != word[i]:
                            new_word = word[:i] + char + word[i + 1:]

                            if new_word in end:
                                return step + 1
                            if new_word in valid_word:
                                next_level.add(new_word)
                                valid_word.remove(new_word)

            # 指针替换
            begin = next_level
            step += 1

        return 0

上面的两个方法在构建 new_word 时都遍历了所有 26 个字母 char,其实我们可以根据 end_word 的去重字符进行状态空间压缩,从而减少无意义的遍历,因为 char not in end_word 则 new_word 必定 not in end_word,从而优化时间复杂度。 

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

2.Min-Gen-Mutation [433]

最小基因突变: https://leetcode.cn/problems/minimum-genetic-mutation/description/ 

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

◆ BFS

class Solution(object):
    def minMutation(self, startGene, endGene, bank):
        """
        :type startGene: str
        :type endGene: str
        :type bank: List[str]
        :rtype: int
        """
        if not bank:
            return -1

        bank = set(bank)
        if endGene not in bank:
            return -1

        stack = [(startGene, 0)]

        while stack:
            gene, level = stack.pop(0)

            for i in range(len(gene)):
                for char in "ACGT":
                    new_gene = gene[:i] + char + gene[i + 1:]

                    if new_gene == endGene:
                        return level + 1

                    if new_gene in bank:
                        stack.append((new_gene, level + 1))
                        bank.remove(new_gene)

        return -1

和上一题异曲同工之妙,只不过从单词接龙变成基因 🧬 接龙,每次修改的地方有限。

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

◆ 双向 BFS

class Solution(object):
    def minMutation(self, startGene, endGene, bank):
        """
        :type startGene: str
        :type endGene: str
        :type bank: List[str]
        :rtype: int
        """
        if not bank:
            return -1

        bank = set(bank)
        if endGene not in bank:
            return -1

        # 初始化首尾
        front, back, step = {startGene}, {endGene}, 0

        while front and back:

            next_front = set()

            # 遍历当前层 Gene
            for gene in front:
                print(gene)
                for i in range(len(gene)):
                    for char in "ACGT":
                        new_gene = gene[:i] + char + gene[i + 1:]
                        # 相遇了
                        if new_gene in back:
                            return step + 1
                        # 下一层突变
                        if new_gene in bank:
                            next_front.add(new_gene)
                            bank.remove(new_gene)

            # 取短的遍历加速
            if len(next_front) > len(back):
                front, back = back, next_front
            else:
                front = next_front

            step += 1

        return -1

和上面异曲同工,老曲新唱,相当于再温习一遍。其加速点就是左右替换,优先遍历可能性少的情况。

Python - 深夜数据结构与算法之 Two-Ended BFS,夜深人静写算法,Python,宽度优先,深度优先,算法,双向BFS

四.总结

这节内容 '双向 BFS' 起始也包含着很多剪枝的策略,所以其也属于优化搜索方式的方法之一,下一节我们介绍高级搜索的最后一块内容: A* 启发式搜索。文章来源地址https://www.toymoban.com/news/detail-810454.html

到了这里,关于Python - 深夜数据结构与算法之 Two-Ended BFS的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python数据结构和算法

    参考 python图解算法 选择/快速排序 哈希表 广度优先搜索算法 迪杰斯特拉算法 贪婪算法 动态规划 K-邻近算法 算法计时 time模块,与算法复杂度 O() [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EIwk2Zdi-1691788469064)(https://facert.gitbooks.io/python-data-str

    2024年02月13日
    浏览(42)
  • Python数据结构与算法

    栈、队列、双端队列和列表都是有序的数据集合, 其元素的顺序取决于添加顺序或移除顺序。一旦某个元素被添加进来,它与前后元素的相对位置将保持不变。这样的数据集合经常被称为线性数据结构。 栈的添加操作和移除操作总发生在同一端。栈中的元素离底端越近,代

    2024年02月02日
    浏览(42)
  • Python数据结构与算法-树

    详情见 https://blog.csdn.net/little_limin/article/details/129845592 Python数据结构与算法-堆排序(NB组)—— 一、树的基础知识 树结构也是链式存储的,与链表的结构相似,只是树存在多个子节点,不是线性的,存在一堆多的情况。与双链表相似,只不过链表节点对应的下一个节点只有一

    2023年04月15日
    浏览(27)
  • python算法与数据结构---动态规划

    记不住过去的人,注定要重蹈覆辙。 对于一个模型为n的问题,将其分解为k个规模较小的子问题(阶段),按顺序求解子问题,前一子问题的解,为后一子问题提供有用的信息。在求解任一子问题时,通过决策求得局部最优解,依次解决各子问题。最后通过简单的判断,得到

    2024年02月20日
    浏览(40)
  • 数据结构与算法之Python实现——队列

    在上一期博客中我们学习了栈这种结构,本期博客将学习一下跟栈很类似的一种结构——队列。 本期知识点: 顺序队列 循环队列 链式队列 队列的应用 ⚪️ 什么是队列? 队列是一种跟栈很相似的结构。我们知道栈是一种 先进后出 的结构,那么队列就像一个排队的队伍一样

    2024年02月06日
    浏览(34)
  • 【数据结构与算法】python实现二分查找

    二分查找 又称折半查找,它是一种效率较高的查找方法 原理:首先,假设表中元素是按升序排列,将表中间位置记录的与查找比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的大于查找,则

    2024年02月05日
    浏览(34)
  • 数据结构与算法Python版:基数排序

    简介 :基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中

    2024年01月24日
    浏览(33)
  • python算法与数据结构(搜索算法和拓扑排序算法)---深度优先搜索

    了解树/图的 深度遍历,宽度遍历 基本原理; 会使用python语言编写 深度遍历,广度遍历 代码; 掌握 拓扑排序算法 搜索引擎 提到搜索两个子,大家都应该会想到搜索引擎,搜索引擎的基本工作步骤; 网页爬取 — 数据预处理 — 排序 — 查询 第一步,网页爬取,非常重要,

    2024年01月20日
    浏览(40)
  • python算法与数据结构---排序和归并排序

    掌握归并排序的基本原理 使用python语言解答归并排序题目 原理及过程 将两个有序的数组合并成一个有序数组称为 从上往下分解:把当前区间一分为二,直至分解为若干个长度为1的子数组 从上往下的合并:两个有序的子区域两两向上合并; 体现了分治思想,稳定排序 复杂

    2024年01月21日
    浏览(58)
  • 【python与数据结构】(leetcode算法预备知识)

    笔记为自我总结整理的学习笔记,若有错误欢迎指出哟~ 1.数字类型: 整数(int):表示整数值,例如 1、-5、100。 浮点数(float):表示带有小数部分的数字,例如 3.14、-0.5、2.0。 复数(complex):表示实部和虚部的复数,例如 2+3j。 2.布尔类型(bool): 表示真(True)或假(

    2024年02月08日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包