diff --git a/chapter12/1_cheapest-flights-within-k-steps.py b/chapter12/1_cheapest-flights-within-k-steps.py new file mode 100644 index 0000000..49a7e66 --- /dev/null +++ b/chapter12/1_cheapest-flights-within-k-steps.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# coding=utf-8 + +####################################################################################### +# Leetcode 787 K 站中转内最便宜的航班 +# +# 题目太长了,瞅这 => https://leetcode-cn.com/problems/cheapest-flights-within-k-stops/ +####################################################################################### + +from typing import List +import collections, heapq + +class Solution: + def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, K: int) -> int: + """ + :type n: int 城市个数 + :type flights: List[List[int]] m个航班 + :type src: int 出发城市 + :type dst: int 到达城市 + :type K: int 最大中转次数 + :rtype int + + (knowledge) + + 思路: + 1. 执行Djikstra算法计算最短距离的算法,过程中传递当前的跳数,当跳数超过k+1时进行剪枝; + 2. 使用最小堆来简化Djikstra代码实现; + 3. 过程中一旦遍历到目的节点,就返回; + """ + + # 这边使用字典来统一表示图 + graph = collections.defaultdict(dict) + + # 构建图 + for u, v, w in flights: + graph[u][v] = w + + # 最小堆中的每个元素为一个三元组,<源节点到当前节点的开销,源节点到当前节点走了几跳,当前节点编号> + pq = [(0, 0, src)] + + while pq: + + # 从最小堆中取出cost最小的元素 + cost, step, place = heapq.heappop(pq) + + # 如果到达当前节点的跳数大于K+1,则跳过 + if step > K + 1: + continue + + if place == dst: + return cost + + # 遍历当前节点的所有邻居 + for neighbour, weight in graph[place].items(): + heapq.heappush(pq, (cost + weight, step + 1, neighbour)) + return -1 + + +if __name__ == '__main__': + solution = Solution() + print(solution.findCheapestPrice(3, [[0, 1, 100], [1, 2, 100], [0, 2, 500]], 0, 2, 1), "= 200") + print(solution.findCheapestPrice(3, [[0, 1, 100], [1, 2, 100], [0, 2, 500]], 0, 2, 0), "= 500") diff --git a/chapter12/2_shortest-bridge.py b/chapter12/2_shortest-bridge.py new file mode 100644 index 0000000..fa4c3f9 --- /dev/null +++ b/chapter12/2_shortest-bridge.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# coding=utf-8 + +####################################################################################### +# Leetcode 934 最短的桥 +# +# 在给定的二维二进制数组 A 中,存在两座岛。(岛是由四面相连的 1 形成的一个最大组。) +# 现在,我们可以将 0 变为 1,以使两座岛连接起来,变成一座岛。 +# 返回必须翻转的 0 的最小数目。(可以保证答案至少是 1。) +# +# 示例 1: +# 输入:[[0,1],[1,0]] +# 输出:1 +# +# 示例 2: +# 输入:[[0,1,0],[0,0,0],[0,0,1]] +# 输出:2 +# +# 示例 3: +# 输入:[[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]] +# 输出:1 +# +# 提示: +# 1. 1 <= A.length = A[0].length <= 100 +# 2. A[i][j] == 0 或 A[i][j] == 1 +####################################################################################### + +from typing import List + +class Solution: + def shortestBridge(self, A: List[List[int]]) -> int: + """ + :type A: List[List[int]] + :rtype int + + (knowledge) + + 思路: + 1. 首先使用DFS的方式找到其中一座岛屿,并将该岛屿内所有块标记为2; + 2. 接着使用BFS的方式逐层向外探索: + - 超出边界则忽略; + - 探索到海域,则将其标记为2,表示已经探索过; + - 当探索到1即终止,返回当前的轮数 + """ + n, queue = len(A), [] + + def dfs(i, j, queue): + if i < 0 or j < 0 or i >= n or j >= n or A[i][j] != 1: + return + A[i][j] = 2 + queue.append((i, j)) + dfs(i - 1, j, queue) + dfs(i + 1, j, queue) + dfs(i, j - 1, queue) + dfs(i, j + 1, queue) + return + + + # 通过dfs找到第一座岛屿 + find = False + for i in range(n): + for j in range(n): + if A[i][j] == 1: + dfs(i, j, queue) + find = True + break + if find: + break + + # 通过BFS搜索另一个岛屿 + s = 0 # 轮数 + while queue: + for _ in range(len(queue)): + i, j = queue.pop(0) + for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: + x, y = i + dx, j + dy + if x < 0 or y < 0 or x >= n or y >= n or A[x][y] == 2: + continue + if A[x][y] == 1: + return s + + # 经过海域,则将其标记为2,表示已经探索过 + A[x][y] = 2 + queue.append((x, y)) + s += 1 + + +if __name__ == '__main__': + solution = Solution() + print(solution.shortestBridge([[0 ,1], [1, 0]]), "= 1") + print(solution.shortestBridge([[0 ,1, 0], [0, 0, 0], [0, 0, 1]]), "= 2") + print(solution.shortestBridge([[1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1]]), "= 1") diff --git a/chapter12/3_top-k-frequent-words.py b/chapter12/3_top-k-frequent-words.py new file mode 100644 index 0000000..40028a0 --- /dev/null +++ b/chapter12/3_top-k-frequent-words.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# coding=utf-8 + +####################################################################################### +# Leetcode 692 前K个高频单词 +# +# 给一非空的单词列表,返回前 k 个出现次数最多的单词。 +# 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。 +# +# 示例 1: +# 输入: ["i", "love", "leetcode", "i", "love", "coding"], k = 2 +# 输出: ["i", "love"] +# 解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。 +# 注意,按字母顺序 "i" 在 "love" 之前。 +# +# 示例 2: +# 输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4 +# 输出: ["the", "is", "sunny", "day"] +# 解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词, +# 出现次数依次为 4, 3, 2 和 1 次。 +# +# 注意: +# 1. 假定 k 总为有效值, 1 ≤ k ≤ 集合元素数。 +# 2. 输入的单词均由小写字母组成。 +# +# 扩展练习: +# 尝试以 O(n log k) 时间复杂度和 O(n) 空间复杂度解决。 +####################################################################################### + +from typing import List +import collections + +class Solution: + def topKFrequent(self, words: List[str], k: int) -> List[str]: + """ + :type words: List[str] + :type k: int + :rtype List[str] + + (knowledge) + + 思路: + 1. 统计每个单词出现的频率; + 2. 然后按照词频和字母序排序(首先按照词频排序,然后如果词频相同就按字母序排序); + 3. 最后返回排序后前k个元素 + """ + count = collections.Counter(words) + candidates = list(count.keys()) + candidates.sort(key=lambda w: (-count[w], w)) + return candidates[:k] + + +if __name__ == '__main__': + solution = Solution() + print(solution.topKFrequent(["i", "love", "leetcode", "i", "love", "coding"], 2), "= [\"i\", \"love\"]")