|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[539. 最小时间差](https://leetcode-cn.com/problems/minimum-time-difference/solution/gong-shui-san-xie-jian-dan-mo-ni-ti-by-a-eygg/)** ,难度为 **中等**。 |
| 4 | + |
| 5 | +Tag : 「模拟」、「排序」、「哈希表」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +给定一个 24ドル$ 小时制(小时:分钟 `"HH:MM"`)的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。 |
| 10 | + |
| 11 | +示例 1: |
| 12 | +``` |
| 13 | +输入:timePoints = ["23:59","00:00"] |
| 14 | + |
| 15 | +输出:1 |
| 16 | +``` |
| 17 | +示例 2: |
| 18 | +``` |
| 19 | +输入:timePoints = ["00:00","23:59","00:00"] |
| 20 | + |
| 21 | +输出:0 |
| 22 | +``` |
| 23 | + |
| 24 | +提示: |
| 25 | +* 2ドル <= timePoints <= 2 * 10^4$ |
| 26 | +* `timePoints[i]` 格式为 `"HH:MM"` |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +### 模拟(排序) |
| 31 | + |
| 32 | +根据题意,我们需要找出「时钟盘」中的夹角最小的两个时间点,其中包括了分布在 `00:00` 左右两侧(横跨了一天)的时间点。 |
| 33 | + |
| 34 | +因此,一种简单的方式是对于每个 $timePoints[i],ドル我们不仅记录「当天该时间点」对应的的偏移量,还记录「下一天该时间点」对应的偏移量。 |
| 35 | + |
| 36 | +处理所有的 $timePoints[i]$ 后,对偏移量进行排序,遍历找到所有相邻元素之间的最小值。 |
| 37 | + |
| 38 | +**代码(感谢 [@Benhao](/u/himymben/) 和 [@🍭可乐可乐吗](/u/littletime_cc/) 同学提供的其他语言版本):** |
| 39 | +```Java |
| 40 | +class Solution { |
| 41 | + public int findMinDifference(List<String> timePoints) { |
| 42 | + int n = timePoints.size() * 2; |
| 43 | + int[] nums = new int[n]; |
| 44 | + for (int i = 0, idx = 0; i < n / 2; i++, idx += 2) { |
| 45 | + String[] ss = timePoints.get(i).split(":"); |
| 46 | + int h = Integer.parseInt(ss[0]), m = Integer.parseInt(ss[1]); |
| 47 | + nums[idx] = h * 60 + m; |
| 48 | + nums[idx + 1] = nums[idx] + 1440; |
| 49 | + } |
| 50 | + Arrays.sort(nums); |
| 51 | + int ans = nums[1] - nums[0]; |
| 52 | + for (int i = 0; i < n - 1; i++) ans = Math.min(ans, nums[i + 1] - nums[i]); |
| 53 | + return ans; |
| 54 | + } |
| 55 | +} |
| 56 | +``` |
| 57 | +- |
| 58 | +```Python3 |
| 59 | +class Solution: |
| 60 | + def findMinDifference(self, timePoints: List[str]) -> int: |
| 61 | + n = len(timePoints) * 2 |
| 62 | + nums, idx = [0] * n, 0 |
| 63 | + for time in timePoints: |
| 64 | + h, m = int(time[:2]), int(time[-2:]) |
| 65 | + nums[idx] = h * 60 + m |
| 66 | + nums[idx + 1] = nums[idx] + 1440 |
| 67 | + idx += 2 |
| 68 | + nums.sort() |
| 69 | + return min(nums[i + 1] - nums[i] for i in range(n - 1)) |
| 70 | +``` |
| 71 | +- |
| 72 | +```Go |
| 73 | +func findMinDifference(timePoints []string) int { |
| 74 | + n := len(timePoints) * 2 |
| 75 | + nums := make([]int, n) |
| 76 | + for i, idx := 0, 0; i < n / 2; i++ { |
| 77 | + ss := strings.Split(timePoints[i], ":") |
| 78 | + h, _ := strconv.Atoi(ss[0]) |
| 79 | + m, _ := strconv.Atoi(ss[1]) |
| 80 | + nums[idx] = h * 60 + m |
| 81 | + nums[idx + 1] = nums[idx] + 1440 |
| 82 | + idx += 2 |
| 83 | + } |
| 84 | + sort.Ints(nums) |
| 85 | + ans := nums[1] - nums[0]; |
| 86 | + for i := 1; i < n - 1; i++ { |
| 87 | + if v := nums[i + 1] - nums[i]; v < ans { |
| 88 | + ans = v |
| 89 | + } |
| 90 | + } |
| 91 | + return ans |
| 92 | +} |
| 93 | +``` |
| 94 | +- |
| 95 | +```C++ |
| 96 | +class Solution { |
| 97 | +public: |
| 98 | + int findMinDifference(vector<string>& timePoints) { |
| 99 | + int n = timePoints.size() * 2; |
| 100 | + vector<int> nums(n); |
| 101 | + for(int i = 0, idx = 0; i < n / 2; i++, idx += 2){ |
| 102 | + string ss = timePoints[i]; |
| 103 | + auto pos = ss.find(':'); |
| 104 | + int h = stoi(ss.substr(0, pos)), m = stoi(ss.substr(pos + 1)); |
| 105 | + nums[idx] = h * 60 + m; |
| 106 | + nums[idx + 1] = nums[idx] + 1440; |
| 107 | + } |
| 108 | + sort(nums.begin(), nums.end()); |
| 109 | + int ans = nums[1] - nums[0]; |
| 110 | + for(int i = 0; i < n - 1; i++){ |
| 111 | + ans = min(ans, nums[i + 1] - nums[i]); |
| 112 | + } |
| 113 | + return ans; |
| 114 | + } |
| 115 | +}; |
| 116 | +``` |
| 117 | +- |
| 118 | +```C |
| 119 | +int min(int a,int b){ return a > b ? b : a; } |
| 120 | +int cmp(const void* a , const void* b){ return (*(int*)a) - (*(int*)b); } |
| 121 | + |
| 122 | +int findMinDifference(char ** timePoints, int timePointsSize){ |
| 123 | + int n = timePointsSize * 2; |
| 124 | + int* nums = (int*) malloc(sizeof(int) * n); |
| 125 | + for(int i = 0, idx = 0; i < n / 2; i++, idx += 2){ |
| 126 | + timePoints[i][2] = '0円'; |
| 127 | + int h = atoi(timePoints[i]), m = atoi(timePoints[i] + 3); |
| 128 | + nums[idx] = h * 60 + m; |
| 129 | + nums[idx + 1] = nums[idx] + 1440; |
| 130 | + } |
| 131 | + qsort(nums, n, sizeof(nums[0]), cmp); |
| 132 | + int ans = nums[1] - nums[0]; |
| 133 | + for(int i = 0; i < n - 1; i++){ |
| 134 | + ans = min(ans, nums[i + 1] - nums[i]); |
| 135 | + } |
| 136 | + free(nums); |
| 137 | + nums = NULL; |
| 138 | + return ans; |
| 139 | +} |
| 140 | +``` |
| 141 | +* 时间复杂度:$O(n\log{n})$ |
| 142 | +* 空间复杂度:$O(n)$ |
| 143 | + |
| 144 | +--- |
| 145 | + |
| 146 | +### 模拟(哈希表计数) |
| 147 | + |
| 148 | +利用当天最多只有 60ドル * 24 = 1440$ 个不同的时间点(跨天的话则是双倍),我们可以使用数组充当哈希表进行计数,同时根据「抽屉原理」,若 $timePoints$ 数量大于 1440ドル,ドル必然有两个相同时间点,用作剪枝。 |
| 149 | + |
| 150 | +然后找到间隔最小两个时间点,这种利用「桶排序」的思路避免了对 $timePoints$ 所对应的偏移量进行排序,而 $O(C)$ 的复杂度使得所能处理的数据范围没有上限。 |
| 151 | + |
| 152 | +代码: |
| 153 | +```Java |
| 154 | +class Solution { |
| 155 | + public int findMinDifference(List<String> timePoints) { |
| 156 | + int n = timePoints.size(); |
| 157 | + if (n >= 1440) return 0; |
| 158 | + int[] cnts = new int[1440 * 2 + 10]; |
| 159 | + for (String s : timePoints) { |
| 160 | + String[] ss = s.split(":"); |
| 161 | + int h = Integer.parseInt(ss[0]), m = Integer.parseInt(ss[1]); |
| 162 | + cnts[h * 60 + m]++; |
| 163 | + cnts[h * 60 + m + 1440]++; |
| 164 | + } |
| 165 | + int ans = 1440, last = -1; |
| 166 | + for (int i = 0; i <= 1440 * 2 && ans != 0; i++) { |
| 167 | + if (cnts[i] == 0) continue; |
| 168 | + if (cnts[i] > 1) ans = 0; |
| 169 | + else if (last != -1) ans = Math.min(ans, i - last); |
| 170 | + last = i; |
| 171 | + } |
| 172 | + return ans; |
| 173 | + } |
| 174 | +} |
| 175 | +``` |
| 176 | +* 时间复杂度:$O(C)$ |
| 177 | +* 空间复杂度:$O(C)$ |
| 178 | + |
| 179 | +--- |
| 180 | + |
| 181 | +### 最后 |
| 182 | + |
| 183 | +这是我们「刷穿 LeetCode」系列文章的第 `No.539` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 184 | + |
| 185 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 186 | + |
| 187 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 188 | + |
| 189 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 190 | + |
0 commit comments