zoukankan      html  css  js  c++  java
  • 182. 跟着三叶学最短路径问题(存图方式)

    三叶最短路径问题博客 "跟着大哥混有饭吃"

    1.图1(跟着三叶学图论最短路算法)

    这是 LeetCode 上的「743. 网络延迟时间」,难度为「中等」    
    
    有 n 个网络节点,标记为1到 n。
    给你一个列表times,表示信号经过 有向 边的传递时间。times[i] = (ui, vi, wi),其中ui是源节点,
    vi是目标节点, wi是一个信号从源节点传递到目标节点的时间。
    现在,从某个节点K发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回-1 。
    
    1 <= k <= n <= 100
    1 <= times.length <= 6000
    times[i].length == 3
    1 <= ui, vi <= n
    ui != vi
    0 <= wi <= 100
    所有 (ui, vi) 对都 互不相同(即,不含重复边)
    
    times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
    times = [[1,2,1]], n = 2, k = 1
    times = [[1,2,1]], n = 2, k = 2
    
    没办法自己不会分析,只能学三叶的分析,假装我会了!!!
    做题首先: 看题目给出的条件,条件k < n <= 100 也就是节点>= 100, times.length <= 6000  边>= 6000
    [从某个节点K发出一个信号。需要多久才能使所有节点都收到信号?] 这句话什么意思呢? 
    举个例子:
    比如太阳到地球需要10s, 到月球需要5s, 到火星需要8s, 那么太阳光最短几秒可以照射到这三个星球,
    直接取太阳光带最远的星球花的时间是不是就好了,也就是到地球的10s.这个题是一个意思,我们只需要求出k点发出的信号到最远的地方所花的时间就好了.
    
    # 存图方式: 重点*****  m 表示节点数, n表示边数
    1. 邻接矩阵(二维数组存图,很方便理解和观看,但是消耗很大,耗时很长O(m**2))
    chart = [[0 for _ in range(n)] for _ in range(n)]
    def add(src, dest, weight):
        chart[src][dest] = weight
    2.邻接表(链表形式存图, 非常适合稀疏图, 消耗很小O(m+n))
        下面我举了一个列子方便理解:
            n = 6  # 节点
            m = 10  # 边 一般会设置大点
            edges = [[0, 1, 100], [1, 2, 100], [0, 2, 500]]
            edges = [[4, 1, 1], [1, 2, 3], [0, 3, 2], [0, 4, 10], [3, 1, 1], [1, 4, 3]]
            idx = 0  # 边编号
            he = [-1 for _ in range(n)]
    
            e = [0 for _ in range(m)]
            ne = [0 for _ in range(m)]
            w = [0 for _ in range(m)]
    
            def add(src, dest, weight):
                nonlocal idx
                e[idx] = dest  # 当前边指向的节点dest
                ne[idx] = he[src]  # 链表的上一个边对应的idx
                he[src] = idx  # 当前起始节点指向的边
                w[idx] = weight  # 当前边的权重
                idx += 1
                """ 先看代码,熟悉一下看下面解释,看不懂继续看while循环代码
                同一个起点, 对应的边的链表:比如从9->3 9 ->4
                idx=0:  
                e[0] = 3   0号边指向3号节点
                ne[0] = he[9] = -1   -1表示结束
                he[9] = 0  9号节点指向0号边
                w[0] = x   0号边的权重
                
                idx = 1
                e[1] = 4   1号边指向4号节点
                ne[1] = he[9] = 0   1号边的上一个边是0号边
                he[9] = 1  9号节点执行1号边
                w[1] = x   1号边的权重
                
                最后发现每he[n]  n一直都表示的其实当前节点, 只不过he[n]的值一直在变换( 相当于链表头结点插入值), he[n]一直更换头结点
                idx = ne[he[n]] 指向的是他的下一个边的编号, 通过这个编号, 我们可以通过dest = e[idx], dest就是n指向的一个节点
                之后在通过ne[idx]拿到下一个idx的编号,一直到节点为-1
                9 -> 4 -> 3 -> -1    看不懂下面有代码while循环中的代码
                """
    
            for edge in edges:
                add(edge[0], edge[1], edge[2])
    
            # 上面解释不是很清楚,直接上代码
            i = he[0]  # 以0号节点为例, i = he[0]取出0号节点的指向的头节点(也就是一个边的编号)(链表里面存储的是边的编号)
            while i != -1:
                b = e[i]  # e中存储的i号边指向的节点
                c = w[i]  # w中存储的是i号边的权重
                print(b, c)
                i = ne[i]  # ne存储0号节点的下一个边(这些边都是从0号节点出发的,只不是使用数组代替链表存储了他们)
    
    3.类(这个方式很少用, O(m))
    class Edge(object):
        def __init__(self, src, dest, weight):
            self.src = src
            self.dest = dest
            self.weight = weight
    

    1.Floyd 算法(邻接矩阵)

    Floyd 算法主要分三步:
    1.初始化图
    distance = [[INF for _ in range(M)] for _ in range(M)]
    distance[i][j] = distance[j][i] = 0 if i == j else INF  # i,j 相等就是同有个节点, 所以为0
    
    2.填充值  (将给出的节点边权关系填充进去)
    for item in times:
        s, t , c = item[0], item[1], item[2]
        distance[s][t] = c
    3.dis[i][j]存储从i->j的最小距离
    他有一个更新条件 if dis[i][j] > dis[i][p] + dis:  dis[i][j] = dis[i][p] + dis, 其实就是根据三角形边推出来的,其中p是中间过渡节点, 所以需要遍历
    
    Floyd 算法会将所有节点之间的最短路径都求出来, 之后只要遍历dist[k][x], 找出k点到其他x节点(需要遍历)的最大值就好了
    def floyd(dis):
        for p in range(1, n + 1):
            for i in range(1, n + 1):
                for j in range(1, n + 1):
                    dis[i][j] = min(dis[i][j], dis[i][p] + dis[p][j])
    
    class Solution(object):
        def networkDelayTime(self, times, n, k):
            """
            :type times: List[List[int]]
            :type n: int
            :type k: int
            :rtype: int
            https://zhuanlan.zhihu.com/p/339542626  Floyd 算法
            """
            M = 110
            INF = float("inf")
            distance = [[INF for _ in range(M)] for _ in range(M)]
            # 初始化图
            for i in range(1, n + 1):
                for j in range(1, n + 1):
                    distance[i][j] = distance[j][i] = 0 if i == j else INF
    
            # 邻接矩阵图
            for item in times:
                s, t , c = item[0], item[1], item[2]
                distance[s][t] = c
    
            def floyd(dis):
                for p in range(1, n + 1):
                    for i in range(1, n + 1):
                        for j in range(1, n + 1):
                            dis[i][j] = min(dis[i][j], dis[i][p] + dis[p][j])
            floyd(distance)
            ans = 0
            for i in range(1, n + 1):
                ans = max(ans, distance[k][i])
            return ans if ans != INF else -1
    

    1.Dijkstra(邻接矩阵)

    https://leetcode-cn.com/problems/network-delay-time/solution/gtalgorithm-dan-yuan-zui-duan-lu-chi-tou-w3zc/

    """743. 网络延迟时间"""
    class Solution(object):
        def networkDelayTime(self, times, n, k):
            INF = float("inf")
            w = [[0 for _ in range(n)] for _ in range(n)]  # 节点之间的权重
            dist = [INF for _ in range(n)]  # 从初始节点到n节点的最多距离
    
            # 初始化图
            for i in range(n):
                for j in range(n):
                    w[i][j] = 0 if i == j else INF
    
            # 添加边权关系
            for item in times:
                s, t, c = item[0] - 1, item[1] - 1, item[2]  # 以为题目是1->n, 需要-1变成0->n-1
                w[s][t] = c
            dist[k-1] = 0
    
            vis = [False for _ in range(n)]
            for i in range(n-1):  # 遍历次数,为节点次数, 每个节点都需要
                x = -1  # 找没被表示的节点,以该节点更新其他节点与初始节点的距离
                for p in range(n):
                    if not vis[p] and (x == -1 or dist[p] < dist[x]): x = p
                vis[x] = True
                for p in range(n):
                    dist[p] = min(dist[p], dist[x] + w[x][p])
    
            ans = max(dist)
            return -1 if ans >= INF / 2 else ans
    

    1.Dijkstra(邻接表)

    class Node(object):
        def __init__(self, x, val):
            self.x = x
            self.v = val
    
        def __gt__(self, other):
            return self.v > other.v
    
    """743. 网络延迟时间"""
    class Solution(object):
        def __init__(self):
            self.n = 110
            self.m = 6010
            self.INF = float("inf")
    
            self.e = [0 for _ in range(self.m)]
            self.ne = [0 for _ in range(self.m)]
            self.he = [-1 for _ in range(self.n)]
            self.w = [0 for _ in range(self.m)]
            self.dist = [self.INF for _ in range(self.n)]  # 从初始节点到n节点的最多距离
            self.idx = 0
    
        def add(self, src, dest, weight):
            self.e[self.idx] = dest
            self.ne[self.idx] = self.he[src]
            self.he[src] = self.idx
            self.w[self.idx] = weight
            self.idx += 1
    
        def networkDelayTime(self, times, n, k):
            """堆优化 Dijkstra(邻接表)"""
            import heapq
            heap = []
            # 存图
            for item in times:
                self.add(item[0], item[1], item[2])
    
            vis = [False for _ in range(self.n)]
            heapq.heappush(heap, Node(k, 0))
            self.dist[k] = 0
    
            while heap:
                t: Node = heapq.heappop(heap)
                node, _ = t.x
                if vis[node]: continue
                vis[node] = True
    
                i = self.he[node]
                while i != -1:
                    j = self.e[i]  # 下一个节点
                    if self.dist[j] > self.dist[node] + self.w[i]:
                        self.dist[j] = self.dist[node] + self.w[i]
                        heapq.heappush(heap, Node(j, self.dist[j]))
                    i = self.ne[i]  # 获取下一个边
    
            ans = max(self.dist[1:n+1])
            return -1 if ans >= self.INF / 2 else ans
    
    
    s1 = Solution()
    times = [[2,1,1],[2,3,1],[3,4,1]]; n = 4; k = 2
    ret = s1.networkDelayTime(times, n, k)
    print(ret)
    

    1.Bellman Ford(类)

    """743. 网络延迟时间"""
    class Edge(object):
        def __init__(self, src, dest, weight):
            self.src = src
            self.dest = dest
            self.w = weight
    
    class Solution(object):
        def networkDelayTime(self, times, n, k):
            """Bellman Ford(类 & 邻接表)"""
            INF = float("inf")
            w = [[0 for _ in range(n)] for _ in range(n)]  # 节点之间的权重
            dist = [INF for _ in range(n+1)]  # 从初始节点到n节点的最多距离
    
            # 初始化图
            for i in range(n):
                for j in range(n):
                    w[i][j] = 0 if i == j else INF
    
            # 添加边权关系
            for item in times:
                s, t, c = item[0] - 1, item[1] - 1, item[2]  # 以为题目是1->n, 需要-1变成0->n-1
                w[s][t] = c
            dist[k] = 0
    
            for i in range(n):
                new_dist = dist[:]  # 确保本次操作不影响之前的dist数据
                for edge in times:
                    src, dest, weight = edge
                    dist[dest] = min(dist[dest], new_dist[src] + weight)
    
            ans = max(dist[1:])
            return -1 if ans >= INF / 2 else ans
    
    
    s1 = Solution()
    times = [[2,1,1],[2,3,1],[3,4,1]]; n = 4; k = 2
    ret = s1.networkDelayTime(times, n, k)
    print(ret)
    

    1. Bellman Ford 邻接表

    class Solution(object):
        def __init__(self):
            self.n = 110
            self.m = 6010
            self.INF = float("inf")
    
            self.e = [0 for _ in range(self.m)]
            self.ne = [0 for _ in range(self.m)]
            self.he = [-1 for _ in range(self.n)]
            self.w = [0 for _ in range(self.m)]
            self.dist = [self.INF for _ in range(self.n)]  # 从初始节点到n节点的最多距离
            self.idx = 0
    
        def add(self, src, dest, weight):
            self.e[self.idx] = dest
            self.ne[self.idx] = self.he[src]
            self.he[src] = self.idx
            self.w[self.idx] = weight
            self.idx += 1
    
        def networkDelayTime(self, times, n, k):
            # 存图
            for item in times:
                self.add(item[0], item[1], item[2])
            self.dist[k] = 0
    
            for p in range(1, n+1):
                new_dist = self.dist[:]
                for q in range(1, n+1):
                    i = self.he[q]
                    while i != -1:
                        b = self.e[i]
                        self.dist[b] = min(self.dist[b], new_dist[q] + self.w[i])
                        i = self.ne[i]
            ans = max(self.dist[1:n+1])
            return -1 if ans >= self.INF / 2 else ans
    
    
    s1 = Solution()
    times = [[2,1,1],[2,3,1],[3,4,1]]; n = 4; k = 2
    ret = s1.networkDelayTime(times, n, k)
    print(ret)
    

    1.SPFA(邻接表)

    https://blog.csdn.net/m15738518751/article/details/47805003  这个细节讲得很好,但是没图
    https://www.cnblogs.com/shadowland/p/5870640.html   这个图很好,但是没讲细节
    class Solution(object):
        def __init__(self):
            self.n = 110
            self.m = 6010
            self.INF = float("inf")
    
            self.e = [0 for _ in range(self.m)]  # 某一条边指向的节点
            self.ne = [0 for _ in range(self.m)]  # 当前边指向的下一个边索引
            self.he = [-1 for _ in range(self.n)]  # 头结点对应的边
            self.w = [0 for _ in range(self.m)]  # 权重
            self.dist = [self.INF for _ in range(self.n)]  # 从初始节点到n节点的最多距离
            self.idx = 0
            self.vis = [False for _ in range(self.n)]
    
        def add(self, src, dest, weight):
            self.e[self.idx] = dest
            self.ne[self.idx] = self.he[src]
            self.he[src] = self.idx
            self.w[self.idx] = weight
            self.idx += 1
    
        def networkDelayTime(self, times, n, k):
            # 存图
            import queue
            for item in times:
                self.add(item[0], item[1], item[2])
            self.dist[k] = 0
    
            heap_q = queue.SimpleQueue()
            heap_q.put(k)
            self.vis[k] = True
            while not heap_q.empty():
                cur = heap_q.get()
                self.vis[cur] = False  # 当前节点拿出来之后,就可以把self.vis置位False了
                new_dist = self.dist[:]
                i = self.he[cur]  # 当前节点指向的边的头idx
                while i != -1:
                    b = self.e[i]  # 指向的节点
                    if self.dist[b] > new_dist[cur] + self.w[i]:
                        if not self.vis[b]:  # 如果下一个节点没在则加入heap_q, 否则只更新距离即可
                            heap_q.put(b)
                            self.vis[b] = True
                        self.dist[b] = new_dist[cur] + self.w[i]
                    i = self.ne[i]
            ans = max(self.dist[1:n+1])
            return -1 if ans >= self.INF / 2 else ans
    
    
    s1 = Solution()
    times = [[2,1,1],[2,3,1],[3,4,1]]; n = 4; k = 2
    ret = s1.networkDelayTime(times, n, k)
    print(ret)
    
  • 相关阅读:
    浅谈软件测试流程
    在9个点上画10条直线,要求每条直线上至少有三个点
    word中快速插入时间
    多核处理器时,__rdtsc()的使用编程珠玑第一章
    解决 error LNK2019: 无法解析的外部符号 问题
    修改IE代理
    overload重载 override覆盖 overwirte重写
    几个题目
    12个球一个天平,现知道只有一个和其它的重量不同,问怎样称才能用三次就找到那个球。
    在link.c中已经include了头文件了,为什么使用ld还无法识别mian和printf?
  • 原文地址:https://www.cnblogs.com/liuzhanghao/p/15210001.html
Copyright © 2011-2022 走看看