Leetcode热题100:图论

Leetcode 200. 岛屿数量

深度优先搜索法:

对于这道题来说,是一个非常经典的图的问题,我们可以先从宏观上面来看问题,也就是说在不想具体算法的前提下,简单的说出如何找到所有的岛屿呢?

如图中所示,首先非常显而易见的要素就是我们一定要遍历图中的每一个坐标,这是肯定的,然后呢对于岛屿的数量,一开始想的肯定就是每当我们访问了一个1的坐标时,我们就找到了一个岛屿,可是因为我们要遍历整个图,所以肯定不能每次遇到一个1就对岛屿的数量进行增加,因为题目说了,横纵连接着的坐标都是1的话都属于同一个岛屿,就像上图一样证明我们只有三个岛屿

那么我们是不是可以像一个办法,当我们找到了一个岛屿的坐标后,把属于这个坐标的岛屿的其他所有值为1的坐标都记录下来,这样以来,意思就是说在我们继续遍历的时候,我们检查每一个坐标时,如果发现这个坐标已经被标记过了,证明他并不是一个新的岛屿而是一个之前就发现过的岛屿的一部分那么就可以了

如何去实现这个标记的方法呢,就可以用到我们的深度优先搜索,从新发现的坐标开始一直找,直到找到了岛屿的边缘或者整个图的边缘之后返回,这样就能找到所有属于相同岛屿的坐标,那么我们可以把主代码写出来了已经

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        res = 0

        # iterate to check every box in the grid to see
        # if it is a start for a new lsland
        for r in range(len(grid)):
            for c in range(len(grid[0])):
                
                # if so, run a dfs to mark all its fellow
                # box and add the 1 to the result
                if grid[r][c] == '1':
                    dfs(grid, r, c)
                    res = res + 1
        
        return res

主体部分的代码非常简单,然后呢我们需要做的就是对于dfs的编写,正如在这个主体代码中写的,你会发现我这里还是用的如果box中存的元素是1,那就最终要增加岛屿数量,这难道不是错的吗?其实这里就隐喻了我们是如何完成对一个岛屿上所有的box进行标记的,与其新建一个空间例如集合然后把dfs中搜索到的box全都加入进去然后每一次循环遍历的时候去看当前的box是否在那个集合中,更好的方法其实就是直接原地更改我们的grid,将搜索到的box中的值从1修改到2,这样这个主体函数中的if 语句就不会被execute了

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:

        def dfs(grid, r, c):
            # base case, do nothing when we
            # reached the edge of entire grid or found water
            if not (0 <= r < len(grid) and 0 <= c < len(grid[0])) or grid[r][c] != '1':
                return
            
            # mark the visited box as 2
            grid[r][c] = '2'

            # call the recursive case on each direction from current box
            dfs(grid, r - 1, c)
            dfs(grid, r + 1, c)
            dfs(grid, r, c - 1)
            dfs(grid, r, c + 1)

        res = 0

        # iterate to check every box in the grid to see
        # if it is a start for a new lsland
        for r in range(len(grid)):
            for c in range(len(grid[0])):
                
                # if so, run a dfs to mark all its fellow
                # box and add the 1 to the result
                if grid[r][c] == '1':
                    dfs(grid, r, c)
                    res = res + 1
        
        return res

这样你就有了整个的代码,其实这种类型的深度优先搜索可以参考我们在二叉树的时候是怎么做的,无非也是一直遍历然后直到我们reach 到了leaf node然后发现新的node是空的时候就直接return,这里不过是多加了两个方向,在二叉树里的recursive case是对左右子节点继续call,这里是对上下左右四个横纵坐标的方向继续call,然后呢条件多加了一个,这里对应的不能超出边界就是二叉树中的if not root, 只是这里还要保证grid[r][c] == 1,不是1的话就不是我们要找的同一个岛屿上的点

时间复杂度 O(m*n):m和n就是grid的长和宽,因为我们要遍历每一个坐标

空间复杂度 O(m*n):在最坏的情况下,如果左上角的box就是陆地1,并且整张图都是1,                                       那么我们就要把每一个box都有一个recursive function,这样在就会用                                     到一个大小为m*n的栈


Leetcode 994. 腐烂的橘子

广度优先搜索法:

这道题的关键在于我们在一开始所给的grid里面存在多个橘子的可能,我们需要在把能弄腐烂的橘子全部弄完之后就返回结果,所以每一分钟里我们需要对所有当前的橘子进行扩散,这里运用深度优先的递归就很麻烦,因为我们每一次逻辑要处理多个搜索

这里我们就使用广度优先,利用队列来对腐烂橘子进行保存并且通过出队来进行遍历然后扩散后把新的腐烂的橘子继续加入队列

这样以来,每一次我们通过向四个方向的检查中发现新的好橘子之后就可以立马对其进行操作,具体分三步,第一步将其加入我们的队列代表下一轮遍历的时候,他会pop出来作为新的坐标然后向四周扩散,第二步将我们的好橘子的count做一个递减,第三步,将grid中其坐标位置的值从1变成2避免后序的遍历中反复visit这个坏橘子

class Solution:
    def orangesRotting(self, grid: List[List[int]]) -> int:
        # we are using a queue to do the bfs
        q = deque()
        fresh_count = 0
        res = 0

        # loop through the gird first to find 
        # the amount of fresh and add the rotten to
        # the queue
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == 1:
                    fresh_count = fresh_count + 1
                elif grid[i][j] == 2:
                    q.append((i, j))


        # loop until we have no more rotten orange
        while q and fresh_count > 0:

            # every time we start pop out q again, we did another search
            res = res + 1

            # for each rotten orango, pop them out
            for _ in range(len(q)):
                box = q.popleft()
                r, c = box[0], box[1]

                # check top
                if r - 1 >= 0 and grid[r - 1][c] == 1:
                    fresh_count = fresh_count - 1
                    q.append((r - 1, c))
                    grid[r - 1][c] = 2

                
                # check right
                if c + 1 < len(grid[0]) and grid[r][c + 1] == 1:
                    fresh_count = fresh_count - 1
                    q.append((r, c + 1))
                    grid[r][c + 1] = 2
                
                # check bot
                if r + 1 < len(grid) and grid[r + 1][c] == 1:
                    fresh_count = fresh_count - 1
                    q.append((r + 1, c))      
                    grid[r + 1][c] = 2

                # check left
                if c - 1 >= 0 and grid[r][c - 1] == 1:
                    fresh_count = fresh_count - 1
                    q.append((r, c - 1))
                    grid[r][c - 1] = 2
        
        # we did not get them all rotton
        if fresh_count > 0:
            return -1
        else:
            return res

时间复杂度 O(m*n):m和n就是grid的长和宽,因为我们要遍历每一个坐标

空间复杂度 O(m*n):在最坏的情况下,一开始所有的橘子都是坏橘子,我们使用的额外空间队列的长度最大就是m*n

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. Two Sum 2. Add Two Numbers 3. Longest Substring Without Repeating Characters 4. Median of Two Sorted Arrays 5. Longest Palindromic Substring 6. ZigZag Conversion 7. Reverse Integer 8. String to Integer (atoi) 9. Palindrome Number 10. Regular Expression Matching 11. Container With Most Water 12. Integer to Roman 13. Roman to Integer 14. Longest Common Prefix 15. 3Sum 16. 3Sum Closest 17. Letter Combinations of a Phone Number 18. 4Sum 19. Remove Nth Node From End of List 20. Valid Parentheses 21. Merge Two Sorted Lists 22. Generate Parentheses 23. Swap Nodes in Pairs 24. Reverse Nodes in k-Group 25. Remove Duplicates from Sorted Array 26. Remove Element 27. Implement strStr() 28. Divide Two Integers 29. Substring with Concatenation of All Words 30. Next Permutation 31. Longest Valid Parentheses 32. Search in Rotated Sorted Array 33. Search for a Range 34. Find First and Last Position of Element in Sorted Array 35. Valid Sudoku 36. Sudoku Solver 37. Count and Say 38. Combination Sum 39. Combination Sum II 40. First Missing Positive 41. Trapping Rain Water 42. Jump Game 43. Merge Intervals 44. Insert Interval 45. Unique Paths 46. Minimum Path Sum 47. Climbing Stairs 48. Permutations 49. Permutations II 50. Rotate Image 51. Group Anagrams 52. Pow(x, n) 53. Maximum Subarray 54. Spiral Matrix 55. Jump Game II 56. Merge k Sorted Lists 57. Insertion Sort List 58. Sort List 59. Largest Rectangle in Histogram 60. Valid Number 61. Word Search 62. Minimum Window Substring 63. Unique Binary Search Trees 64. Unique Binary Search Trees II 65. Interleaving String 66. Maximum Product Subarray 67. Binary Tree Inorder Traversal 68. Binary Tree Preorder Traversal 69. Binary Tree Postorder Traversal 70. Flatten Binary Tree to Linked List 71. Construct Binary Tree from Preorder and Inorder Traversal 72. Construct Binary Tree from Inorder and Postorder Traversal 73. Binary Tree Level Order Traversal 74. Binary Tree Zigzag Level Order Traversal 75. Convert Sorted Array to Binary Search Tree 76. Convert Sorted List to Binary Search Tree 77. Recover Binary Search Tree 78. Sum Root to Leaf Numbers 79. Path Sum 80. Path Sum II 81. Binary Tree Maximum Path Sum 82. Populating Next Right Pointers in Each Node 83. Populating Next Right Pointers in Each Node II 84. Reverse Linked List 85. Reverse Linked List II 86. Partition List 87. Rotate List 88. Remove Duplicates from Sorted List 89. Remove Duplicates from Sorted List II 90. Intersection of Two Linked Lists 91. Linked List Cycle 92. Linked List Cycle II 93. Reorder List 94. Binary Tree Upside Down 95. Binary Tree Right Side View 96. Palindrome Linked List 97. Convert Binary Search Tree to Sorted Doubly Linked List 98. Lowest Common Ancestor of a Binary Tree 99. Lowest Common Ancestor of a Binary Search Tree 100. Binary Tree Level Order Traversal II
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值