leetcode每周3道(八)图之最短路

743. 网络延迟时间

题目描述

有 n 个网络节点,标记为 1 到 n。
给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。
现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1 。
示例1

思路

题目实际是求节点 K 到其他所有点中最远的距离,那么首先需要求出节点 KK到其他所有点的最短路,然后取最大值即可。

  • 单源最短路问题可以使用 Dijkstra 算法,其核心思路是贪心算法。流程如下:
  • 首先,Dijkstra 算法需要从当前全部未确定最短路的点中,找到距离源点最短的点 x。
  • 其次,通过点 xx更新其他所有点距离源点的最短距离。例如目前点 A 距离源点最短,距离为 3;有一条 A->B 的有向边,权值为 1,那么从源点先去 A 点再去 B 点距离为 3 + 1 = 4,若原先从源点到 B 的有向边权值为 5,那么我们便可以更新 B 到源点的最短距离为 4。
  • 当全部其他点都遍历完成后,一次循环结束,将 xx 标记为已经确定最短路。进入下一轮循环,直到全部点被标记为确定了最短路。

代码转化:

  • 首先,Dijkstra 算法需要存储各个边权,由于本题节点数量不超过 100100,所以代码中使用了邻接矩阵 graph[i][j] 存储从点 i 到点 j 的距离。若两点之间没有给出有向边,则初始化为 inf。
  • 算法还需要记录所有点到源点的最短距离,代码中使用了 dist[i] 数组存储源点到点 i 的最短距离,初始值也全部设为 inf。由于本题源点为 K,所以该点距离设为 0。
  • 其次,Dijkstra 算法需要标记某一节点是否已确定了最短路,在代码中使用了 visit[i] 数组存储,若已确定最短距离,则值为 true,否则值为 false。

代码实现

class Solution(object):
    def networkDelayTime(self, times, n, k):
        """
        :type times: List[List[int]]
        :type n: int
        :type k: int
        :rtype: int
        """
        #邻接矩阵的构建
        graph = [[float('inf')] * n for _ in range(n)]       
        for x,y,w in times:
            graph[x-1][y-1] = w

        #原点到各个位置的最近距离矩阵
        dist = [float('inf')] * n
        dist[k-1] = 0

        #访问flag数组
        visit = [0]*n

        #dijstra算法
        for _ in range(n):
            x = -1
            #找到目前距离源点最近的点
            for i,d in enumerate(dist):
                if (not visit[i]) and (d<dist[x] or x == -1):
                    x = i
            visit[x] = 1
            #更新dist数组
            for i,d in enumerate(graph[x]):
                dist[i] = min(dist[i],dist[x]+d)
                #当前距离原点最近的点x距离原点的距离为dist[x] 再加上x点距离i点的weight 进行更新
        ans = max(dist)
        return ans if ans != float('inf') else -1

结果

结果1

847. 访问所有节点的最短路径

题目描述

存在一个由 n 个节点组成的无向连通图,图中的节点按从 0 到 n - 1 编号。
给你一个数组 graph 表示这个图。其中,graph[i] 是一个列表,由所有与节点 i 直接相连的节点组成。
返回能够访问所有节点的最短路径的长度。你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边。
示例2

思路

  • 由于题目需要我们求出「访问所有节点的最短路径的长度」,并且图中每一条边的长度均为 1,因此我们可以考虑使用广度优先搜索的方法求出最短路径。
  • 在常规的广度优先搜索中,我们会在队列中存储节点的编号。对于本题而言,最短路径的前提是「访问了所有节点」,因此除了记录节点的编号以外,我们还需要记录每一个节点的经过情况。因此,我们使用三元组 (u,mask,dist) 表示队列中的每一个元素,其中:
    u 表示当前位于的节点编号;mask 是一个长度为 nn 的二进制数,表示每一个节点是否经过。如果 mask 的第 i 位是 1,则表示节点 i 已经过,否则表示节点 i 未经过;dist 表示到当前节点为止经过的路径长度。
  • 这样一来,我们使用该三元组进行广度优先搜索,即可解决本题。初始时,我们将所有的 (i, 2^i, 0)放入队列,表示可以从任一节点开始。在搜索的过程中,如果当前三元组中的 mask 包含 n 个 1(即mask=2^n−1),那么我们就可以返回dist 作为答案。

细节

  • 为了保证广度优先搜索时间复杂度的正确性,即同一个节点 u 以及节点的经过情况mask只被搜索到一次,我们可以使用数组或者哈希表记录(u,mask) 是否已经被搜索过,防止无效的重复搜索。
  • bfs先到达的一定更优,这个常识,因为水波式扩散
  • seen[v][mask_v]记录的是跑到v节点时状态为mask_v(通过状压记录状态,如果某个节点跑到过就让mask_v对应二进制位为1),而!seen[v][mask_v]则是看对于跑到当前节点v状态为mask_v的情况之前是否发生过,根据性质1,如果之前发生过,那么肯定之前的更优(准确说是不会更劣),那么用之前那个就可以,那当前这个情况就可以忽略了(这个也保证了不会绕圈圈)
  • if (mask == (1 << n) - 1) {
    ans = dist;
    break;
    }
    也是根据性质1,如果所有点都被跑到过一次了,那么第一次达成该状态的跑法一定最优.

代码实现

class Solution(object):
    def shortestPathLength(self, graph):
        """
        :type graph: List[List[int]]
        :rtype: int
        """
        n = len(graph)
        que = deque((i,1<<i,0) for i in range(n))
        seen = {(i,1<<i) for i in range(n)}

        while(que):
            u,mask,dist = que.popleft()
            if mask == (1<<n)-1:
                return dist
                
            for v in graph[u]:
                mask_v = mask | (1<<v)
                if (v,mask_v) not in seen:
                    que.append((v,mask_v,dist+1))
                    seen.add((v,mask_v))               

结果

结果2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值