From 2fc5195a384f5900c282ec99e96217bc0e7fc593 Mon Sep 17 00:00:00 2001 From: SunnyQjm Date: Mon, 22 Jun 2020 20:50:51 +0800 Subject: [PATCH] add: chapter13 --- chapter13/1_network-delay-time.py | 63 +++++++++++++ .../2_shortest-path-visiting-all-nodes.py | 94 +++++++++++++++++++ chapter13/3_approximation-method.py | 33 +++++++ 3 files changed, 190 insertions(+) create mode 100644 chapter13/1_network-delay-time.py create mode 100644 chapter13/2_shortest-path-visiting-all-nodes.py create mode 100644 chapter13/3_approximation-method.py diff --git a/chapter13/1_network-delay-time.py b/chapter13/1_network-delay-time.py new file mode 100644 index 0000000..9b011f4 --- /dev/null +++ b/chapter13/1_network-delay-time.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# coding=utf-8 + +####################################################################################### +# Leetcode 743 网络延迟时间 +# 参见(有图例,这边不好展示) => https://leetcode-cn.com/problems/network-delay-time/ +# +# 有 N 个网络节点,标记为 1 到 N。 +# 给定一个列表 times,表示信号经过有向边的传递时间。 times[i] = (u, v, w),其中 u 是源节点,v 是目标节点, w 是一个信号从源节点传递到目标节点的时间。 +# 现在,我们从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1。 +# +####################################################################################### + +from typing import List +import collections, heapq + +class Solution: + def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int: + """ + :type times: List[List[int]] + :type N: int + :type K: int + :rtype int + + (knowledge) + + 思路: + 1. 用Dijkstra算法求节点K到其它所有节点的最短距离; + 2. 最后返回所有最短距离里面最大的即可; + """ + + # 构造图 + graph = collections.defaultdict(dict) + for u, v, w in times: + graph[u][v] = w + + + visited = {} + + # 一个最小堆,用来存储Dijkstra算法执行过程中的候选节点 + pq = [(0, K)] + + while pq: + distance, node = heapq.heappop(pq) + + # 跳过已遍历的节点 + if node in visited: + continue + + visited[node] = distance + + for neighbour, time in graph[node].items(): + if neighbour not in visited: + # 遍历每个邻居的时候都会执行push操作,可能有一个点被push若干次,但是由于堆的特性,最先被访问到的一定是距离最小的那个 + heapq.heappush(pq, (distance + time, neighbour)) + + # 如果节点K可到达所有节点,则返回最远的距离,否则返回-1 + return max(visited.values()) if len(visited) == N else -1 + + +if __name__ == '__main__': + solution = Solution() + print(solution.networkDelayTime([[2,1,1],[2,3,1],[3,4,1]], 4, 2), "= 2") diff --git a/chapter13/2_shortest-path-visiting-all-nodes.py b/chapter13/2_shortest-path-visiting-all-nodes.py new file mode 100644 index 0000000..e111741 --- /dev/null +++ b/chapter13/2_shortest-path-visiting-all-nodes.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# coding=utf-8 + +####################################################################################### +# Leetcode 847 访问所有节点的最短路径 +# +# 给出 graph 为有 N 个节点(编号为 0, 1, 2, ..., N-1)的无向连通图。 +# graph.length = N,且只有节点 i 和 j 连通时,j != i 在列表 graph[i] 中恰好出现一次。 +# 返回能够访问所有节点的最短路径的长度。你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边。 +# +# 示例 1: +# 输入:[[1,2,3],[0],[0],[0]] +# 输出:4 +# 解释:一个可能的路径为 [1,0,2,0,3] +# +# 示例 2: +# 输入:[[1],[0,2,4],[1,3,4],[2],[1,2]] +# 输出:4 +# 解释:一个可能的路径为 [0,1,4,2,3] +# +# 提示: +# 1. 1 <= graph.length <= 12 +# 2. 0 <= graph[i].length < graph.length +####################################################################################### + +import collections +from typing import List + +class Solution: + def shortestPathLength(self, graph: List[List[int]]) -> int: + """ + :type graph: List[List[int]] + :rtype int + + (knowledge) + + 思路: + 1. 首先引入一个用位压缩来表示当前图中节点访问状态的一个概念,何谓位压缩? + 题中提到,图的节点个数不会超过十二个,那我们完全可以用一个数字的前N位来标记图中哪些节点已经被访问,而哪些没有: + 例如:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 => 即 表示编号为1和3的节点已经访问过了,其它节点还没有被访问 + + 2. 然后我们定一个遍历过程中的状态元组,(位压缩表示,节点编号) => 表示当前处于某个节点上,且位压缩对应的是当前哪些节点已经遍历过了 + 例如:(7, 0) => 表示当前处于编号为0的节点,并且编号为0、1、2的节点已经访问过了 + 7 => 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 + + 3. 根据上面的讨论,我们结束的目标 END 就是得到一个位压缩表示,其值为 (1 << N) - 1 + 例如:N = 8 + (1 << N) => 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 + (1 << N) - 1 => 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 + + 4. 我们初始时以每一个节点为起点,同时向外泛洪扩张,直到某一时刻得到结束状态 END + """ + # 得到图节点个数 + N = len(graph) + + # 结束标记 + END = (1 << N) - 1 + + # 初始以图中所有节点为出发点 + queue = [(1 << i, i) for i in range(N)] + + # 用一个字典记录当前图要达到某一个状态,所需的最短路径长度 + dist = collections.defaultdict(lambda: N * N) + + # 达到某一个节点访问了,其它节点没有访问的状态,只要以该节点为起点就可,所以距离为0 + for i in range(N): + dist[(1 << i, i)] = 0 + + while queue: + + # 取出队头元素 + cover, node = queue.pop(0) + + # 获取要达到该状态目前记录的最短距离,默认为N*N + d = dist[(cover, node)] + + # 如果当前状态是目标状态,则直接返回其对应距离 + if cover == END: + return d + + # 遍历其所有邻居 + for child in graph[node]: + newCover = cover | (1 << child) + + # 如果通过该邻居到达一个新的状态所需的总得距离小于之前记录的距离,则加入到队列当中 + if d + 1 < dist[(newCover, child)]: + dist[(newCover, child)] = d + 1 + queue.append((newCover, child)) + + +if __name__ == '__main__': + solution = Solution() + print(solution.shortestPathLength([[1,2,3],[0],[0],[0]]), "= 4") + print(solution.shortestPathLength([[1],[0,2,4],[1,3,4],[2],[1,2]]), "= 4") diff --git a/chapter13/3_approximation-method.py b/chapter13/3_approximation-method.py new file mode 100644 index 0000000..58ed2ea --- /dev/null +++ b/chapter13/3_approximation-method.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# coding=utf-8 + +####################################################################################### +# 近似算法的应用 => 集合覆盖问题 +# +# tip: 场景和题目描述件ppt +####################################################################################### + +if __name__ == '__main__': + states_needed = set(["mt", "wa", "or", "id", "nv", "ut", "ca", "az"]) + stations = {} + stations["knoe"] = set(["id", "nv", "ut"]) + stations["ktwo"] = set(["wa", "id", "mt"]) + stations["kthree"] = set(["or", "nv", "ca"]) + stations["kfour"] = set(["nv", "ut"]) + stations["kfive"] = set(["ca", "az"]) + + final_stations = set() + + while states_needed: + best_station = None + states_covered = set() + for station, states in stations.items(): + covered = states_needed & states + if len(covered) > len(states_covered): + best_station = station + states_covered = covered + states_needed -= states_covered + final_stations.add(best_station) + + + print(final_stations)