【Leetcode】top 100 图论

基础知识补充

1.图分为有向图和无向图,有权图和无权图;

2.图的表示方法:邻接矩阵适合表示稠密图,邻接表适合表示稀疏图;

邻接矩阵:

邻接表:

基础操作补充

1.邻接矩阵:

python 复制代码
class GraphAdjacencyMatrix:
    def __init__(self, num_vertices):
        self.num_vertices = num_vertices
        self.matrix = [[0] * num_vertices for _ in range(num_vertices)]

    def add_edge(self, start, end):       # 无向图
        self.matrix[start][end] = 1
        self.matrix[end][start] = 1

2.邻接表:

python 复制代码
from collections import defaultdict

class GraphAdjacencyList:
    def __init__(self):
        self.graph = defaultdict(list)

    def add_edge(self, start, end):        # 无向图
        self.graph[start].append(end)
        self.graph[end].append(start)

3.图的遍历:

python 复制代码
# 深度优先搜索(DFS):
# 从上到下,递归或栈实现
def dfs(graph, start, visited=None):
    if visited is None:
        visited = set()
    visited.add(start)
    print(start, end=" ")
    for neighbor in graph[start]:
        if neighbor not in visited:
            dfs(graph, neighbor, visited)

# 广度优先搜索(BFS):
# 从左到右,队列实现
from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    visited.add(start)
    while queue:
        current = queue.popleft()
        print(current, end=" ")
        for neighbor in graph[current]:
            if neighbor not in visited:
                queue.append(neighbor)
                visited.add(neighbor)
题目
200 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。

方法一:深度优先搜索 DFS

若当前点是岛屿时,向上下左右四个点做深度搜索;终止条件:越界;当前是水;

python 复制代码
class Solution(object):
    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        def dfs(nums, x, y):
            if x<0 or x>len(nums)-1: return 
            if y<0 or y>len(nums[0])-1: return 
            if nums[x][y] =='0':return 
            else:
                nums[x][y] = '0'    # 必须先置0,否则会在两个'1'间连续递归至超过栈长
                dfs(nums, x-1, y)
                dfs(nums, x+1, y)
                dfs(nums, x, y-1)
                dfs(nums, x, y+1)
            
        cnt = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '1':
                    dfs(grid, i, j)
                    cnt += 1
        return cnt

方法二:广度优先搜索 BFS

若当前点是岛屿时,将其上下左右四个点都加入队列;终止条件:越界;当前是水;

python 复制代码
class Solution(object):
    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        def bfs(nums, x, y):
            queue = [(x, y)]
            while queue:
                (x, y) = queue.pop(0)
                if x<0 or x>len(nums)-1: continue 
                elif y<0 or y>len(nums[0])-1: continue 
                elif nums[x][y] =='0':continue 
                else:
                    nums[x][y] = '0'    # 必须先置0,否则会在两个'1'间连续递归至超过栈长
                    queue.append((x-1, y))
                    queue.append((x+1, y))
                    queue.append((x, y-1))
                    queue.append((x, y+1))
        
        cnt = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '1':
                    bfs(grid, i, j)
                    cnt += 1
        return cnt
994 腐烂的橘子

在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:

  • 0 代表空单元格;
  • 1 代表新鲜橘子;
  • 2 代表腐烂的橘子。

每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1

第一次遍历将所有新鲜橘子腐烂,统计腐烂次数;第二次遍历统计是否还有剩余的新鲜橘子;(若初始就不含有新鲜橘子呢?)

一次遍历统计新鲜橘子数量的同时记录腐烂橘子的位置(队列);

遍历队列,若当前位置是腐烂橘子则将其上下左右四个点入队,若当前位置是新鲜橘子则将新鲜橘子数量-1再将其上下左右四个点入队;需要将处理过的位置的值置为0,代表不再处理;

python 复制代码
class Solution(object):
    def orangesRotting(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        
        cnt, queue = 0, []
        m, n = len(grid), len(grid[0])
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 1:
                    cnt += 1
                elif grid[i][j] == 2:
                    queue.append([i,j])

        if cnt == 0: return 0

        time, stack = -1, []
        while queue:
            [x, y] = queue.pop(0)
            if -1<x<m and -1<y<n and grid[x][y]:
                if grid[x][y] == 1: cnt -= 1
                grid[x][y] = 0            # 不再处理这个点
                stack.append([x-1, y])
                stack.append([x+1, y])
                stack.append([x, y-1])
                stack.append([x, y+1])
            if not queue and stack:
                queue = stack
                time += 1 
                stack = []

        return -1 if cnt else time

计算遍历深度用BFS

207 课程表

你这个学期必须选修 numCourses 门课程,记为 0numCourses - 1 。在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai必须 先学习课程 bi

  • 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false

方法一:广度优先搜索

python 复制代码
from collections import deque
from collections import defaultdict

class Solution(object):
    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        degree = [0]*numCourses    
        maps = defaultdict(list)   
        queue = deque()
        for cur, pre in prerequisites:
            degree[cur] += 1                      # 统计每门课的先修课程数
            maps[pre].append(cur)                 # 记录基础课和对应的进阶课
        for i in range(numCourses):
            if degree[i] == 0: queue.append(i)    # 无先修课程(基础课)时入队
        count = 0
        while queue:
            course = queue.popleft()
            count += 1
            for i in maps[course]:                # 将以course为基础课的进阶课的先修课数-1
                degree[i] -= 1
                if degree[i] == 0:                # 已修完全部基础课
                    queue.append(i)  
        return count == numCourses

方法二:深度优先搜索

python 复制代码
class Solution(object):
    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        degree = [0]* numCourses
        maps = defaultdict(list)
        def dfs(i):
            if degree[i]==-1: return False    # degree[i]==-1 表示会陷入循环
            if degree[i]==1: return True      # degree[i]==1 表示能完成课 
            degree[i]=-1                      # 防止 1-0-1 转回来的情况
            for pre in maps[i]:               # 遍历每门基础课
                if not dfs(pre): return False
            degree[i]=1                       # 该门课可以完成
            return True
        
        for cur, pre in prerequisites:        # 记录先修课和其基础课程
            maps[cur].append(pre)
        for i in range(numCourses):           # 遍历每门课
            dfs(i)
        return sum(degree) == numCourses      # 若每门课都完成应该全为1
208 实现Trie(前缀树)

Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

请你实现 Trie 类:

  • Trie() 初始化前缀树对象。
  • void insert(String word) 向前缀树中插入字符串 word
  • boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false
  • boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false

核心:使用「边」来代表有无字符,使用「点」来记录是否为「单词结尾」以及「其后续字符串的字符是什么」

python 复制代码
class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

class Trie(object):
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word):
        """
        :type word: str
        :rtype: None
        """
        node = self.root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNode()
            node = node.children[c]
        node.is_end = True

    def searchPrefix(self, word):
        node = self.root
        for c in word:
            if c not in node.children: return None
            node = node.children[c]
        return node

    def search(self, word):
        """
        :type word: str
        :rtype: bool
        """
        node = self.searchPrefix(word)
        return node is not None and node.is_end

    def startsWith(self, prefix):
        """
        :type prefix: str
        :rtype: bool
        """
        node = self.searchPrefix(prefix)
        return node is not None
额外补充

flood fill 带你学习Flood Fill算法与最短路模型 - 时间最考验人 - 博客园 (cnblogs.com)

相关推荐
05候补工程师2 分钟前
【408考研·数据结构专题】二叉树、树与森林、线索树及哈夫曼树核心考点与秒杀技巧深度总结
数据结构·经验分享·笔记·考研·算法
吃好睡好便好10 分钟前
矩阵的加减运算
开发语言·人工智能·学习·线性代数·算法·matlab·矩阵
吃好睡好便好17 分钟前
提取矩阵特定多行元素
开发语言·线性代数·算法·matlab·矩阵
葫三生24 分钟前
多模态视角下的一部当代东方创世史诗 ——《论三生原理》?(扩版)
人工智能·科技·算法·机器学习·开源
stsdddd32 分钟前
【YOLO算法包裹背包行李箱塑料袋包装纸盒快递盒带目标检测数据集】
算法·yolo·目标检测
洛水水36 分钟前
【力扣100题】52.最小路径和
算法·leetcode
快手技术36 分钟前
将DSA注意力引入多模态,快手Keye2.0开启强化推理新范式
算法
圣保罗的大教堂1 小时前
leetcode 3043. 最长公共前缀的长度 中等
leetcode
码之气三段.1 小时前
牛客周赛 Round 145-E(写了200行的史山)
算法·深度优先
Hwang2521 小时前
Attention-04-decoder部分
算法