Python 刷题笔记:广度优先搜索专题

昨天看过了简单题汇聚的深度优先搜索专题,今天来体验下简单级别的广度优先搜索专题。老样子,先熟悉下术语概念:

广度优先搜索算法(英语:Breadth-First Search,缩写为BFS),又译作宽度优先搜索,或横向优先搜索,是一种图形搜索算法。简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。广度优先搜索的实现一般采用open-closed表。
BFS是一种盲目搜索法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能地址,彻底地搜索整张图,直到找到结果为止。BFS并不使用经验法则算法。
维基百科:广度优先搜索算法

其实现方法逻辑如下:

  1. 首先将根节点放入队列中
  2. 从队列中取出第一个节点,并检验它是否为目标
    a. 如果找到目标,则结束搜索并回传结果
    b. 否则将它所有尚未检验过的直接子节点加入队列中
  3. 若队列为空,表示整张图都检查过了——亦即图中没有欲搜索的目标。结束搜索并回传“找不到目标”
  4. 重复步骤2

我翻看了下 LeetCode 中几道该算法中等难度的题,跪在当场不能动,所以今天就先拿两道简单题目来开路,之后再研究加大难度的。

题目一

第 107 题:二叉树的层次遍历 II

难度:简单

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回其自底向上的层次遍历为:

[
  [15,7],
  [9,20],
  [3]
]

#来源:力扣(LeetCode)
#链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii

题目分析

我们在之前二叉树专题二中解 Leet Code 第 102 题 二叉树的层序遍历时曾误打误撞用到了该算法,这里自底向上返回,简单些就是将层序遍历的结果倒序返回即可。

代码实现

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
    	# 空根节点直接返回[]
        if not root:
            return []
        # 结果列表
        result = [] 
        # 层列表       
        level = [root]
        # 只要层列表中还有节点就循环
        while level!=[]:
        	# 将层列中节点值取出来放到tmp列表中
            tmp = [itm.val for itm in level]
            # 将tmp列表加入到结果中
            result.append(tmp)
            # 新的层列表
            new_level = []
            # 遍历当前层列表中的节点
            for node in level:
            	# 如果节点左子节点非空
                if node.left!=None:
                	# 将左子节点加入新层列表中
                    new_level.append(node.left)
                # 如果节点的右子节点非空
                if node.right!=None:
                	# 将右子节点加入新层列表中
                    new_level.append(node.right)
            # 新生成的层列表赋值给 level 代入下层循环
            level = new_level
        # 将记录的结果倒序输出        
        return result[::-1]

提交测试表现:

执行用时 : 36 ms, 在所有 Python3 提交中击败了 90.83% 的用户
内存消耗 : 14 MB, 在所有 Python3 提交中击败了 6.25% 的用户

这解法的思路与广度优先算法的设计基本一致,由根节点起,拿到结果;将其未检验的子节点加入队列继续取结果,一直到全部节点遍历完成。但题意限制,并没有出现最上方实现方法中的“如果找到目标,则结束搜索并回传结果”,下面这个题目则完美契合此描述。

题目二

第 111 题:二叉树的最小深度

难度:简单

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

示例:

给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
   
#来源:力扣(LeetCode)
#链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree

返回它的最小深度 2.

题目分析

寻找最小子树高度,在广度优先搜索的过程中,找到没有子节点的节点,即可“结束搜索并回传结果”。同时在遍历时,也无需多做处理,记录下层级高度即可。

代码实现

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def minDepth(self, root: TreeNode) -> int:
    	# 根节点为空直接返回0
        if not root:
            return 0
		# 二叉树深度或子树高度
        height = 0
        # 层列表
        level = [root]
        # 层列表不为空就循环
        while level:
        	# 高度自增1
            height+=1
            # 新的层列表
            new_level = []
            for node in level:
            	# 如果层列表中的节点无子节点
                if node.left==None and node.right==None:
                	# 返回高度,任务结束
                    return height
                # 若存在左子节点
                if node.left!=None:
                	# 将左子节点存入新层列表中
                    new_level.append(node.left)
                # 若存在右子节点
                if node.right!=None:
                	# 将右子节点存入新层列表中
                    new_level.append(node.right)
            # 更新层列表
            level = new_level

提交测试表现:

执行用时 : 48 ms, 在所有 Python3 提交中击败了 91.12% 的用户
内存消耗 : 14.8 MB, 在所有 Python3 提交中击败了 12.50% 的用户

这题的解法就极贴切地再现了广度优先搜索的流程:根节点放入队列,取出检验是否符合目标;若未达成目标,队列中加入其子节点,取出检验,若达到目标返回结果,若未达到目标则继续重复过程。

结论

今天选取的两道题目是非常符合基本的广度优先搜索算法设计的题目,省时高效地既把题目刷了,又加深了对算法的练习和理解。

但更多涉及到该算法的复杂些题目,看着就脑壳疼。我们目前算是处于第一轮熟悉 LeetCode 各种专题题型的阶段,所以优先选取力所能及、便于算法理解的题目。等到之后有实力和时间了,再来硬刚那些难题吧。

你可能感兴趣的:(LeetCode)