力扣刷题记录&整理——(十二)Advanced Graphs


前言

整理力扣刷题思路。

  • 语言:python
  • 题库:来自neetcode: link

一、预备知识

1.最小生成树

最小生成树 (MST) 是指在一个加权连通图中,找到一个无环的子图,使得这个子图包含了图中的所有节点,并且边的总权重最小。具体来说,MST 是一个连通树,它连接了图中的所有节点,但是没有形成环路,同时它的边的权重之和是最小的。

以下是一些关于 MST 的重要性质:

  1. 可能的多样性:对于一个有 n 个顶点的图,每个生成树都有 n-1 条边。有时候,图中可能存在多个权重相同的最小生成树。如果图中所有边的权重都相同,那么每个生成树都是最小生成树。

  2. 唯一性:如果每条边的权重都不同,那么最小生成树是唯一的。这在现实情况下通常成立,例如电信公司铺设电缆的例子,不太可能存在两条完全相同成本的路径。这个性质也适用于生成森林。

2.Prim算法

Prim算法是一种贪心算法,用于在连通的无向图中找到最小生成树(MST)。MST是一个连接了图中所有顶点的树,同时使得树中边的总权重最小。

以下是Prim算法的工作原理和步骤:

  1. 选择起始顶点:首先,随机选择一个顶点作为MST的起始顶点。
  2. 迭代直到所有顶点都包含在MST中
    • 寻找连接树顶点和非树顶点的边:在每一步中,找到连接已经包含在MST中的顶点和尚未包含在MST中的顶点的边。
    • 选择最小权重的边:从这些边中选择权重最小的边。
    • 将选定的边添加到MST中:如果添加这条边不会形成环路,就将这条边和它的另一个顶点添加到MST中。
  3. 返回MST:当所有顶点都被包含在MST中时,算法结束。

二、解题思路

1.BFS

787.cheapest-flights-within-k-stops

有 n 个城市通过一些航班连接。给你一个数组 flights ,其中 flights[i] = [fromi, toi, pricei] ,表示该航班都从城市 fromi 开始,以价格 pricei 抵达 toi。

现在给定所有的城市和航班,以及出发城市 src 和目的地 dst,你的任务是找到出一条最多经过 k 站中转的路线,使得从 src 到 dst 的 价格最便宜 ,并返回该价格。 如果不存在这样的路线,则输出 -1。
link

class Solution:
    def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
        adjacent = [[] for _ in range(n)]
        #记录每个城市可到达的地方及花费
        for flight in flights:
            adjacent[flight[0]].append((flight[1],flight[2]))
        #记录从初始位置到每个城市的最小花费,这个列表在剪枝中很重要
        cur_prices = [float('inf')]*n
        
        cnt = 0
        queue = adjacent[src]
        Min = float('inf')
        while cnt<=k and queue:
            for _ in range(len(queue)):
                city,price = queue.pop(0)
                if price >= Min:
                    continue
                elif city==dst:
                    Min = min(price,Min)
                else:
                    for nxt,pri in adjacent[city]:
                        if pri+price < Min and pri+price < cur_prices[nxt]:
                            queue.append((nxt,pri+price))
                            cur_prices[nxt] = pri+price
            cnt += 1
        return Min if Min<float('inf') else -1

743.network-delay-time

有 n 个网络节点,标记为 1 到 n。

给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。

现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1 。
link

class Solution:
    def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
        adjacent = [[] for _ in range(n)]
        for u,v,w in times:
            adjacent[u-1].append((v-1,w))
        
        queue = [(k-1,0)]
        #记录传递到每个节点的最短时间
        cur_time = [float('inf')]*n
        cur_time[k-1] = 0
        while queue:
            node,time = queue.pop(0)
            for nxt,t in adjacent[node]:
                if cur_time[nxt] > t+time:
                    cur_time[nxt] = t+time
                    queue.append((nxt,t+time))
        
        cur_time = set(cur_time)
        #仍为无穷大的节点代表未被传递到
        return max(cur_time) if float('inf') not in cur_time else -1

2.DFS

332.reconstruct-itinerary

给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前。
假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。
link

class Solution:
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:
        mapping = collections.defaultdict(list)
        for fro,to in tickets:
            mapping[fro].append(to)

        for fro in mapping:
            mapping[fro].sort()
        
        ans = []
        def dfs(pos):
            while mapping[pos]:
                dfs(mapping[pos].pop(0))
            ans.insert(0,pos)
        
        dfs('JFK')
        return ans

参考:link

3.Prim’s

1584.min-cost-to-connect-all-points

给你一个points 数组,表示 2D 平面上的一些点,其中 points[i] = [xi, yi] 。

连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 :|xi - xj| + |yi - yj| ,其中 |val| 表示 val 的绝对值。

请你返回将所有点连接的最小总费用。只有任意两点之间 有且仅有 一条简单路径时,才认为所有点都已连接。
link

class Solution:
    def minCostConnectPoints(self, points: List[List[int]]) -> int:
        '''
        prim算法的思路是n个点需要n-1条边连接
        随机选一个点,将此点加入已访问集合,同时将它与其他未访问点的距离入堆
        堆pop出的未访问点作为下一个点,重复上面的操作
        这样每次得到的距离都是新的点与已访问点的最小距离
        '''
        n = len(points)
        visited = set()
        minH = [[0,0]]
        ans = 0
        while len(visited)<n:
            dist,i = heapq.heappop(minH)
            if i in visited:
                continue
            visited.add(i)
            ans += dist
            for j in range(n):
                if j not in visited:
                    x1,y1 = points[i]
                    x2,y2 = points[j]
                    dist = abs(x1-x2) + abs(y1-y2)
                    heapq.heappush(minH,[dist,j])
        return ans

参考neetcode页面的解说视频

778.swim-in-rising-water

在一个 n x n 的整数矩阵 grid 中,每一个方格的值 grid[i][j] 表示位置 (i, j) 的平台高度。

当开始下雨时,在时间为 t 时,水池中的水位为 t 。你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。当然,在你游泳的时候你必须待在坐标方格里面。

你从坐标方格的左上平台 (0,0) 出发。返回 你到达坐标方格的右下平台 (n-1, n-1) 所需的最少时间 。
link

class Solution:
    def swimInWater(self, grid: List[List[int]]) -> int:
        ans = 0
        hp = [[grid[0][0],0,0]]
        visited = set((0,0))

        while hp:
            height,i,j = heapq.heappop(hp)
            ans = max(ans,height)
            if i==j==len(grid)-1:
                return ans
            
            for i1,j1 in [(i-1,j),(i+1,j),(i,j-1),(i,j+1)]:
                if 0<=i1<len(grid) and 0<=j1<len(grid) and (i1,j1) not in visited:
                    visited.add((i,j))
                    heapq.heappush(hp, [grid[i1][j1],i1,j1])
        
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值