leetcode刷题:广度/深度优先搜索

系列文章目录

leetcode刷题:第一周
leetcode刷题:第二周
leetcode刷题:二分查找
leetcode刷题:双指针
leetcode刷题:广度/深度优先搜索



前言

随着开学,博主学校实验课也开始开展,并且有道理课程最多的大二,再加上准备六月份考英语四级,时间就偷偷溜走了,博客和算法都有一些拉下来。
这周简单记录一些深度/广度优先遍历的搜题题


一、深度/广度优先遍历

1.岛屿数量

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
输出:1
示例 2:

输入:grid = [
[“1”,“1”,“0”,“0”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“1”,“0”,“0”],
[“0”,“0”,“0”,“1”,“1”]
]
输出:3

class Solution:
    def __init__(self):
        self.grid = [
            ["1", "1", "1", "1", "0"],
            ["1", "1", "0", "1", "0"],
            ["1", "1", "0", "0", "0"],
            ["0", "0", "0", "0", "0"]
        ]
    def dfs(self, grid, r, c):
        grid[r][c] = 0
        nr = len(grid)
        nc = len(grid[0])
        for x, y in [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)]:
            if 0 <= x < nr and 0 <= y < nc and grid[x][y] == "1":
                self.dfs(grid, x, y)

    def numIslands(self):
        grid = self.grid
        # 算法部分
        nr = len(grid)
        nc = len(grid[0])
        num_islands = 0
        if nr == 0:
            return 0
        #遍历每一格,当出现岛屿进行深度优先遍历
        for r in range(nr):
            for c in range(nc):
                if grid[r][c] == "1":
                    num_islands += 1
                    self.dfs(grid, r, c)

        return num_islands

if __name__ == '__main__':
    s=Solution()
    p=s.numIslands()
    print(p)

2.省份数量

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

示例 1:

输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
示例 2:

输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3

class Solution:
    def __init__(self):
        self.isConnected = [[1, 1, 0], [1, 1, 0], [0, 0, 1]]

    def findCircleNum(self):
        isConnected = self.isConnected

        # 算法结构(没看懂)
        def dfs(i):
            for j in range(provinces):
                if isConnected[i][j] == 1 and j not in visited:
                    visited.add(j)
                    dfs(j)

        provinces = len(isConnected)
        visited = set()
        circles = 0

        for i in range(provinces):
            if i not in visited:
                dfs(i)
                circles += 1
        return circles

3.填充每个节点的下一个右侧节点指针 II

给定一个二叉树

struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。
进阶:

你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

示例:

输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),’#’ 表示每层的末尾。

class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next


class Solution:
    def connect(self, root: 'Node') -> Node:
        # 算法部分(没看懂)
        if not root:
            return None
        cur = root
        while cur:
            dummy = Node(-1)
            pre = dummy
            while cur:
                if cur.left:
                    pre.next = cur.left
                    pre = pre.next
                if cur.right:
                    pre.next = cur.right
                    pre = pre.next
                cur = cur.next
            cur = dummy.next
        return root

简单说明一下,第一次没看懂,同一父节点,左孩子指向父节点的右孩子,不同父节点,左孩子指向父节点的next(父节点的兄弟节点)的左孩子。

4.另一棵树的子树

给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

示例 1:

输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true
示例 2:

输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
输出:false

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isSubtree(self, root: TreeNode, subRoot: TreeNode) -> bool:
        return self.preorder(root, subRoot)
    
    def preorder(self, root, subRoot):
        if not root: return False
        resleft = self.preorder(root.left, subRoot)
        resright = self.preorder(root.right, subRoot)
        # 如果 根节点就不同 就直接返回左右子树的结果 跳过这一个root所在的子树
        if root.val != subRoot.val: return resleft or resright
        # 如果 根节点相同 就可以开始一次判断
        return resleft or resright or self.helper(root, subRoot)
        
    def helper(self, root, subRoot):
        # 保证 子树的构造相同
        if not root and not subRoot:
            return True
        if not root or not subRoot:
            return False
        # 保证 子树的值 都是相同的
        return root.val == subRoot.val and self.helper(root.left, subRoot.left) and self.helper(root.right, subRoot.right)

5.二进制矩阵中的最短路径

给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1 。

二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:

路径途经的所有单元格都的值都是 0 。
路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。
畅通路径的长度 是该路径途经的单元格总数。

示例 1:

输入:grid = [[0,1],[1,0]]
输出:2
示例 2:

输入:grid = [[0,0,0],[1,1,0],[1,1,0]]
输出:4
示例 3:

输入:grid = [[1,0,0],[1,1,0],[1,1,0]]
输出:-1

class Solution:
    def __init__(self):
        self.grid = [[0, 0, 0], [1, 1, 0], [1, 1, 0]]

    def shortestPathBinaryMatrix(self):
        grid = self.grid
        # 算法部分
        n = len(grid)
        if grid[0][0] or grid[n - 1][n - 1] != 0:
            return -1
        if n == 1:
            return 1

        start = 1
        dxy = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]
        x1 = 0
        x2 = 0
        que = [[x1, x2]]
        while que:
        	#两层while 保证将上一次压入的全部弹出后在累加
            m = len(que)
            #采用广度优先遍历,最先找到几位最短路径
            while m:
                m -= 1
                x, y = que.pop(0)
                if x == n - 1 and y == n - 1:
                    return start
                for dx, dy in dxy:
                    x1 = x + dx
                    x2 = y + dy
                    if 0 <= x1 < n and 0 <= x2 < n and grid[x1][x2] == 0:
                        grid[x1][x2] = 1
                        que.append([x1, x2])
            start += 1
        return -1


if __name__ == '__main__':
    s = Solution()
    p = s.shortestPathBinaryMatrix()
    print(p)

6.被围绕的区域

给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ ,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

示例 1:

输入:board = [[“X”,“X”,“X”,“X”],[“X”,“O”,“O”,“X”],[“X”,“X”,“O”,“X”],[“X”,“O”,“X”,“X”]]
输出:[[“X”,“X”,“X”,“X”],[“X”,“X”,“X”,“X”],[“X”,“X”,“X”,“X”],[“X”,“O”,“X”,“X”]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
示例 2:

输入:board = [[“X”]]
输出:[[“X”]]

class Solution:
    def __init__(self):
        self.board = [["X", "X", "X", "X"], ["X", "O", "O", "X"], ["X", "X", "O", "X"], ["X", "O", "X", "X"]]

    def solve(self):
        board = self.board
        # 算法部分
        if not board:
            return

        def dfs(x, y):
            if not 0 <= x < n or not 0 <= y < m or board[x][y] != 'O':
                return
            board[x][y] = 'A'
            dfs(x + 1, y)
            dfs(x - 1, y)
            dfs(x, y + 1)
            dfs(x, y - 1)

        n, m = len(board), len(board[0])

        for i in range(n):
            dfs(i, 0)
            dfs(i, m - 1)
        for i in range(m - 1):
            dfs(0, i)
            dfs(n - 1, i)
        for i in range(n):
            for j in range(m):
                if board[i][j] == 'A':
                    board[i][j] = 'O'
                elif board[i][j] == 'O':
                    board[i][j] = 'X'

遍历四个边界,把不惜要改变的从O变成A,剩下的O就都是需要填充的,然后遍历整个数组,将O编程X,A在变回O。

7.所有可能的路径

给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序)

graph [i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。

示例 1:

输入:graph = [[1,2],[3],[3],[]]
输出:[[0,1,3],[0,2,3]]
解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3
示例 2:

输入:graph = [[4,3,1],[3,2,4],[3],[4],[]]
输出:[[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]

class Solution:
    def __init__(self):
        self.graph = [[1, 2], [3], [3], []]

    def allPathsSourceTarget(self):
        graph = self.graph
        # 算法目录
        ans = []
        stk = []

        def dfs(x):
            if x == len(graph) - 1:
                ans.append(stk[:])
                return
            for y in graph[x]:
                stk.append(y)
                dfs(y)
                stk.pop()

        stk.append(0)
        dfs(0)
        return ans

总结

学校课程加重了,刷题可能要拉下一些了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值