leetcode刷题笔记-DFS/BFS 2

2101. Detonate the Maximum Bombs

在这里插入图片描述

class Solution:
    def maximumDetonation(self, bombs: List[List[int]]) -> int:
        def detonate(i, j):
            a, b, c = bombs[i]
            x, y, z = bombs[j]
            return (a-x)**2 + (b-y)**2 <= c * c
        
        neighs = collections.defaultdict(list)
        i = 0
        n = len(bombs)

        for i in range(n):
            for j in range(n):
                if i == j: continue
                if detonate(i, j):
                    neighs[i].append(j)
    
        re = 0
        for i in range(n):
            count = 0
            layer = [i] # bfs
            visited = set()
            while layer:
                j = layer.pop(0)
                if j in visited: continue 
                count += 1
                visited.add(j)
                for ni in neighs[j]:
                    layer.append(ni)
            re = max(re, count)
            if re == n:
                return re
        return re

1197. Minimum Knight Moves

在这里插入图片描述 在这里插入图片描述

class Solution:
    def minKnightMoves(self, x: int, y: int) -> int:
        '''
        X,Y四象限的四面是对称的,只要BFS一面, bfs不要走重复的路径
        '''
        q = [(0, 0)]  
        level = 0
        x, y = abs(x), abs(y)
        visited = set((0, 0))
        while q:
            nq = []
            for a, b in q:
                if a == x and b == y:
                    return level
                else:
                    for dx, dy in [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1, 2)]:
                        na, nb = a + dx, b + dy
                        if na >= -1 and nb >= -1 and (na, nb) not in visited:  # 必须包涵-1的位置,不然[3,0]3步走不到
                            nq.append([na, nb])
                            visited.add((na, nb))
            q = nq
            level += 1

1192. Critical Connections in a Network

在这里插入图片描述
在这里插入图片描述

class Solution:
# https://www.youtube.com/watch?v=mKUsbABiwBI 
    def criticalConnections(self, n: int, connections: List[List[int]]) -> List[List[int]]:
        # 找环,属于环的部分的边全部排除,剩下的
        
        # create graph
        graph = collections.defaultdict(list)
        for a, b in connections:
            graph[a].append(b)
            graph[b].append(a)
        
        self.start = 0
        mark = {}
        update = {}  # dfs返回上来的最小值
        discard = set()
        def dfs(node, pre): # 先把所有的点按照dfs访问的顺序 mark 为从0 到n-1,再返回这个点reach到的最小值
             
            if node not in mark:
                mark[node] = self.start
                self.start += 1
            else:
                return mark[node]
            
            minV = mark[node]
            for neig in graph[node]:
                if neig != pre:
                    backV = dfs(neig, node)
                    if backV <= mark[node]:  # <= 不能只是<
                        discard.add(tuple(sorted((node, neig))))  # 记录下要discard的edge 要排序
                        minV = min(minV, backV)
            update[node] = minV
            return minV
                
        dfs(0, -1)
        
        re = []
        # print(discard)
        for a, b in connections:
            if tuple(sorted([a,b])) not in discard:
                re.append([a, b])
        return re

711. Number of Distinct Islands II

在这里插入图片描述
在这里插入图片描述

class Solution:
    def numDistinctIslands2(self, grid: List[List[int]]) -> int:
        '''
        思路就是每个island计算island里面所有点和点之间的距离,这些距离的counter,会是Unique的
        '''
        R, C = len(grid), len(grid[0])
        hasIsland = 0
        def getIsland(r, c, nodeList):  # dfs
            nodeList.append((r, c))
            grid[r][c] = 0
            for nr, nc in [(r+1, c), (r-1, c), (r, c+1), (r, c-1)]:
                if 0 <= nr < R and 0 <= nc < C and grid[nr][nc]:
                    getIsland(nr, nc, nodeList)
         
        def computeNodeDistance(nodeList, counter):  # 计算所有点之间的距离,包括点和自己,因为有的island只有一个点
            for i in range(len(nodeList)):
                for j in range(i, len(nodeList)):
                    dis = (nodeList[i][0] - nodeList[j][0])**2 + (nodeList[i][1] - nodeList[j][1])**2
                    counter[dis] += 1
                    
        distinct = collections.defaultdict(int)     
        for r in range(R):
            for c in range(C):
                if grid[r][c] == 1:
                    hasIsland = 1
                    nodeList = []
                    getIsland(r, c, nodeList)
                    counter = collections.defaultdict(int)
                    computeNodeDistance(nodeList, counter)
                    # print(counter)
                    distinct[tuple([(k, counter.get(k)) for k in sorted(counter)])] += 1  # 这个counter 得排序最后distinct的key才是有序的
        # for k in distinct:
        #     print(k)
        #     print(distinct[k])
        #     print("----")
        return len([k for k in distinct]) or hasIsland
            

1376. Time Needed to Inform All Employees

在这里插入图片描述
在这里插入图片描述

class Solution:
    def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int:
        # 自己做的不是简洁的写法,简洁的要参考dfs的解法
        m2s = collections.defaultdict(list)  # manager to sub map
        s2m = {}  # sub to his manager
        for i, m in enumerate(manager):
            m2s[m].append(i)
            s2m[i] = m
        
        # bfs, count the total time of each path till leaf
        count = collections.defaultdict(int)  # key is the employeeid, value it the count of time from top till this employee
        count[-1] = 0  # for edge case that max(count.values())  count.values() count be empty
        queue = [headID]
        
        while queue:
            eid = queue.pop(0)
            pre = s2m[eid]
            if pre != -1:
                count[eid] += informTime[pre] + count[pre]  # the time his manager need to infom him + the total previosu time
            # print(eid, pre)
            # print(count)
            if m2s[eid]:  # if he has subs
                # add all his subs to the queue
                    queue.extend(m2s[eid])
        
        return max(count.values()) 
    '''
    dfs
    
    
    
        graph = defaultdict(list)  # manager to subs 
        for i in range(len(manager)):
            graph[manager[i]].append(i)
        
        def dfs(u):
            ans = 0
            for v in graph[u]:
                ans = max(dfs(v) + informTime[u], ans)
            return ans
        
        return dfs(headID)
    '''

2115. Find All Possible Recipes from Given Supplies

在这里插入图片描述

class Solution:
    def findAllRecipes(self, recipes: List[str], ingredients: List[List[str]], supplies: List[str]) -> List[str]:
 
        # have a map to map from recip to ingres which are not in supplies
        # dfs recip
        ingres = {recipe: [] for recipe in recipes}  # recipe to ingres map (elimiate those ingre in supplies)
        supplies = set(supplies)
        re = []
        for i, recip in enumerate(recipes):
            for ingre in ingredients[i]:
                if ingre not in supplies:
                    ingres[recip].append(ingre if ingre in ingres else recip)  # 如果不在recipes里面也不在supplies里面,就放自己
                    #这样的话由于21行,dfs的时候就一定能会返回false
                    
        print(ingres)  
        can_make = {}
        def dfs(recip):
            if recip in can_make:
                return can_make[recip]
            
            can_make[recip] = False
            if all(dfs(ingre) for ingre in ingres[recip]):  # all([]) = True
                # print(recip, ingres[recip])
                can_make[recip] = True
            
            return can_make[recip]
        
        
        for recip in recipes:
            if dfs(recip):
                re.append(recip)
        
        return re

2096. Step-By-Step Directions From a Binary Tree Node to Another

在这里插入图片描述
在这里插入图片描述

# 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 getDirections(self, root: Optional[TreeNode], startValue: int, destValue: int) -> str:
        
        """Return lowest common ancestor of start and dest nodes."""
        def lca(node):  
            
            if not node or node.val in (startValue, destValue):
                return node
            left, right = lca(node.left), lca(node.right)
            
            return node if left and right else (left or right)
        
        common = lca(root)
        
        
        stack = [(common, "")]  # node, path
        path_up = path_down = ""

        while stack:  # dfs
            node, path = stack.pop()
            if node.val == startValue:
                path_up = path
            if node.val == destValue:
                path_down = path
            
            if node.left: stack.append((node.left, path + "L"))
            if node.right: stack.append((node.right, path + "R"))
            
            
        return "U" * len(path_up) + path_down

818. Race Car

在这里插入图片描述
在这里插入图片描述

class Solution:

    def racecar(self, T: int, depth = 0) -> int:
        # T > 0
        q = collections.deque([[ 0, 1 ]])
        seen = set('0,1')
        while True:
            k = len(q)
            while k:
                [ pos, vel ] = q.popleft()
                if pos == T:
                    return depth  # 🎯 target T found
                cand = []  # next layer
                if pos + vel > 0:  # prune, check the dp idea,  go back to somewhere>=0 
                    cand.append([ pos + vel, 2 * vel ])  # A
                cand.append([ pos, 1 if vel < 0 else -1 ])  # R
                for pos, vel in cand:
                    if f'{pos},{vel}' not in seen:
                        q.append([ pos, vel ])
                        seen.add(f'{pos},{vel}')
                k -= 1
            depth += 1
        return -1
    
    
  
'''
https://leetcode.com/problems/race-car/discuss/227415/Figures-to-make-the-DP-solution-more-straightforward  
    dp:
       1.  either go forward until pass the target and then go back
        2. Or go forward until one step before target, then go back to somewhere >=0, then go forward again to the target.
      
'''
 
    dp = {0: 0}
    def racecar(self, t):
        if t in self.dp:
            return self.dp[t]
        n = t.bit_length()
        if 2**n - 1 == t:  # 0, 1, 3, 7 = 2**n-1
            self.dp[t] = n
        else:
            self.dp[t] = self.racecar(2**n - 1 - t) + n + 1  # n step + 1 R
            for m in range(n - 1):
                self.dp[t] = min(self.dp[t], self.racecar(t - 2**(n - 1) + 2**m) + n + m + 1)  # link  里面的case1
        return self.dp[t] 

1293. Shortest Path in a Grid with Obstacles Elimination

在这里插入图片描述

from collections import deque
class Solution:
    def shortestPath(self, grid: List[List[int]], k: int) -> int:
        '''
        Solution Explanation
Because we are trying to find the shortest path, use BFS here to exit immediately when a path reaches the bottom right most cell.
Use a set to keep track of already visited paths. We only need to keep track of the row, column, and the eliminate obstacle usage count. We don't need to keep track of the steps because remember we are using BFS for the shortest path. That means there is no value storing a 4th piece of the data, the current steps. This will reduce the amount of repeat work.
m = rows
n = columns
k = allowed elimination usages

Time Complexity
O(m*n*k) time complexity
This is because for every cell (m*n), in the worst case we have to put that cell into the queue/bfs k times.

Runtime: 68 ms, faster than 33.33% of Python3 online submissions

Space Complexity
O(m*n*k) space complexity
This is because for every cell (m*n), in the worst case we have to put that cell into the queue/bfs k times which means we need to worst case store all of those steps/paths in the visited set.
        '''
        if len(grid) == 1 and len(grid[0]) == 1:
            return 0

        queue = deque([(0,0,k,0)])  # 看下面
        visited = set([(0,0,k)])

        if k > (len(grid)-1 + len(grid[0])-1):
            return len(grid)-1 + len(grid[0])-1

        while queue:
            row, col, eliminate, steps = queue.popleft()
            for new_row, new_col in [(row-1,col), (row,col+1), (row+1, col), (row, col-1)]:
                if (new_row >= 0 and
                    new_row < len(grid) and
                    new_col >= 0 and
                    new_col < len(grid[0])):
                    if grid[new_row][new_col] == 1 and eliminate > 0 and (new_row, new_col, eliminate-1) not in visited:
                        visited.add((new_row, new_col, eliminate-1))
                        queue.append((new_row, new_col, eliminate-1, steps+1))
                    if grid[new_row][new_col] == 0 and (new_row, new_col, eliminate) not in visited:
                        if new_row == len(grid)-1 and new_col == len(grid[0])-1:
                            return steps+1
                        visited.add((new_row, new_col, eliminate))
                        queue.append((new_row, new_col, eliminate, steps+1))

        return -1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值