hot100-图论

200. 岛屿数量

200. 岛屿数量 - 力扣(LeetCode)

深搜版本:

python 复制代码
class Solution(object):
    def dfs(self, grid, x, y, n, m):
        grid[x][y] = '0'
        for i,j in [[x+1,y],[x-1,y],[x,y+1],[x,y-1]]:
            if 0<=i<n and 0<=j<m and grid[i][j] == '1':
                self.dfs(grid,i,j,n,m)

    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        # 本质是找连通分量的数量
        # 题目没说是否可以修改原数组,我们默认可以修改
        # 注意,如果不能修改,那么需要使用visited数据进行访问节点的标记
        # 这里,为了节省空间,直接修改原图
        n = len(grid)
        if n == 0:
            return 0
        m = len(grid[0])
        res = 0
        for i in range(n):
            for j in range(m):
                if grid[i][j] == '1':
                    res +=1
                    self.dfs(grid,i,j, n, m)
        return res

时间复杂度:O(MN) # 每个节点过一遍

空间复杂度:O(MN) # 最坏情况:全是陆地,栈全存一遍

广搜版本:

python 复制代码
class Solution(object):
    def numIslands(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: int
        """
        n = len(grid)
        if n == 0:
            return 0
        m = len(grid[0])
        res = 0
        for i in range(n):
            for j in range(m):
                if grid[i][j] == '1':
                    res +=1
                    # 广搜用队列
                    grid[i][j] = '0'
                    queue = [(i,j)]
                    while queue:
                        x,y = queue.pop(0)
                        for u,v in [[x+1,y],[x-1,y],[x,y+1],[x,y-1]]:
                            if 0<=u<n and 0<=v<m and grid[u][v] == '1':
                                grid[u][v] = '0'
                                queue.append((u,v))
        return res

时间复杂度:O(MN) # 每个节点过一遍

空间复杂度:O(min(M,N)) # 最坏情况:全是陆地,队列最大就是存一行或一列

994. 腐烂的橘子

994. 腐烂的橘子 - 力扣(LeetCode)

python 复制代码
class Solution(object):
    def orangesRotting(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        # 多源广度优先搜索
        n,m = len(grid), len(grid[0])  # 题目明确m,n 均>1
        queue = collections.deque()
        for i in range(n):
            for j in range(m):
                if grid[i][j] == 2:
                    queue.append((i,j,0))  # 需要存一下时间
        t = 0
        while queue:
            x,y,t = queue.popleft()
            for i,j in [[x+1,y],[x-1,y],[x,y+1],[x,y-1]]:
                if 0<=i<n and 0<=j<m and grid[i][j] == 1:
                    grid[i][j] = 2
                    queue.append((i,j,t+1))
        # 如果循环结束,说明最后一个t就是res
        # 如果不连通 可能有一些橘子永远无法腐烂
        if any(1 in row for row in grid):
            return -1
        return t

时间复杂度:O(nm)

空间复杂度:O(nm) # 最差是O(nm)

207. 课程表

207. 课程表 - 力扣(LeetCode)

有向无环图 -> 线性拓扑排序 【我们要证明有向图无环!】

深搜版本:

构建图:[a,b] 修完b才能修a,所以b->a

对于图中的任意一个节点,在搜索的过程中有三种状态:

  • 未搜索:还没有搜索到这个节点;
  • 搜索中:搜索过这个节点,但还没有回溯到该节点,即该节点还没有入栈,还有相邻的节点没有搜索完成);一旦搜索中发现这样的节点,就意味着沿着该节点出发回到了该节点,出现了环。
  • 已完成:我们搜索过并且回溯过这个节点,即该节点已经入栈,该节点没有环,此时新入的节点的先后不会居于该节点之后。

在每一轮的搜索搜索开始时,任取一个「未搜索」的节点开始进行DFS。将当前搜索的节点 u 标记为「搜索中」,遍历该节点的每一个相邻节点 v:

  • 如果 v 为「未搜索」,搜索 v,待搜索完成回溯到 u;
  • 如果 v 为「搜索中」,就找到了图中的一个环;
  • 如果 v 为「已完成」,那么说明 v 已经在栈中了,而 u 还不在栈中,要么是先修u,v在u之后,要么是二者顺序无关。

当 u 的所有相邻节点都为「已完成」时,将 u 放入栈中,并将其标记为「已完成」。

python 复制代码
class Solution(object):
    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        # visited数组:0 未搜索 1 搜索中 2 已完成
        visited = [0 for _ in range(numCourses)]
        edges = collections.defaultdict(list)
        self.res = True
        # 这里如果只是想判断是否有环,不需要栈来存储
        # 把已完成的节点压入栈中,然后从栈顶到栈底,其实就是一条可行的链式拓扑结构
        st = []

        # 邻接表存图
        for u,v in prerequisites:
            edges[v].append(u)

        def dfs(u):
            visited[u] = 1
            for v in edges[u]:
                if not visited[v]:
                    dfs(v)
                    if not self.res:
                        return
                elif visited[v] == 1:
                    # 出现了环
                    self.res = False
                    return
            visited[u] = 2
            st.append(u)
        
        for i in range(numCourses):
            if self.res and not visited[i]:
                dfs(i)  # 有可能是非连通图
        return self.res

时间复杂度:O(n+m) # n是课程数,m是先修课程数

空间复杂度:O(n+m)

广搜版本:

广搜版本更加符合拓扑排序的直觉。

使用一个队列来进行广搜。初始时,所有入度为 0 的节点都被放入队列中,作为拓扑排序最前面的节点,并且它们之间的相对顺序无关紧要。

在广度优先搜索的每一步中,取出队首的节点 u,将 u 的所有相邻节点的入度减少 1【移除下一个节点的入边】。如果某个相邻节点 v 的入度变为 0,那么就将 v 放入队列中。

在广度优先搜索的过程结束后。如果答案中包含了这 n 个节点,那么就找到了一种拓扑排序,否则说明图中存在环。

python 复制代码
class Solution(object):
    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        edges = collections.defaultdict(list)
        indge = [0 for _ in range(numCourses)]
        res = []
        # 如果只是想得到是否无环,仅仅计数看过的节点个数;但是如果想得到拓扑排序,需要用数组记录
        for u,v in prerequisites:
            edges[v].append(u)
            indge[u] +=1
        queue = collections.deque([u for u in range(numCourses) if indge[u]==0])
        while queue:
            u = queue.popleft()
            res.append(u)
            for v in edges[u]:
                indge[v] -=1
                if indge[v] ==0:
                    queue.append(v)
        return len(res) == numCourses

时间复杂度:O(n+m) # n是课程数,m是先修课程数

空间复杂度:O(n+m)

208. 前缀树

208. 实现 Trie (前缀树) - 力扣(LeetCode)

python 复制代码
class Trie(object):
    def __init__(self):
        self.children = [None]*26
        self.isEnd = False

    def insert(self, word):
        node = self  # 真的好好品!
        for c in word:
            ch = ord(c) - ord('a')
            if not node.children[ch]:
                node.children[ch] = Trie()
            node = node.children[ch]
        node.isEnd = True
    
    def _searchPrefix(self, prefix):
        node = self
        for c in prefix:
            ch = ord(c) - ord('a')
            if not node.children[ch]:
                return None
            node = node.children[ch]
        return node

    def search(self, word):
        return self._searchPrefix(word) is not None and self._searchPrefix(word).isEnd
        
    def startsWith(self, prefix):
        return self._searchPrefix(prefix) is not None

时间复杂度:初始化为 O(1),其余操作为 O(n),n是每次插入或查询的word的长度。

空间复杂度:O(T⋅Σ),其中 T为所有已经插入字符串的长度之和,Σ 为字符集的大小,这里是26。

相关推荐
tod1131 小时前
力扣基础算法分类刷题:位运算、数学、数组与字符串详解
算法·leetcode·职场和发展
熬了夜的程序员1 小时前
【LeetCode】118. 杨辉三角
linux·算法·leetcode
智算菩萨1 小时前
规模定律的边际递减与后训练时代的理论重构
人工智能·算法
小龙报1 小时前
【51单片机】51 单片机 IIC 协议深度解析:时序实现 + GXHT3L 连续转换模式 + 数据解析
c语言·数据结构·stm32·单片机·嵌入式硬件·物联网·51单片机
kanhao1001 小时前
电平交叉采样 (Level-Crossing Sampling)
算法·fpga开发·fpga
Hcoco_me1 小时前
图像分割:目标检测、语义分割和实例分割
人工智能·深度学习·算法·目标检测·计算机视觉·目标跟踪
iAkuya1 小时前
(leetcode)力扣100 69有效的括号(栈)
算法·leetcode·职场和发展
破烂pan1 小时前
Python 实现 HTTP Client 的常见方式
开发语言·python·http
We་ct1 小时前
LeetCode 21. 合并两个有序链表:两种经典解法详解
前端·算法·leetcode·链表·typescript