最小区间
你有 k 个升序排列的整数数组。找到一个最小区间,使得 k 个列表中的每个列表至少有一个数包含在其中。
我们定义如果 b-a < d-c 或者在 b-a == d-c 时 a < c,则区间 [a,b] 比 [c,d] 小。
示例 1:
输入:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
输出: [20,24]
解释:
列表 1:[4, 10, 15, 24, 26],24 在区间 [20,24] 中。
列表 2:[0, 9, 12, 20],20 在区间 [20,24] 中。
列表 3:[5, 18, 22, 30],22 在区间 [20,24] 中。
注意:
- 给定的列表可能包含重复元素,所以在这里升序表示 >= 。
- 1 <= k <= 3500
- -105 <= 元素的值 <= 105
- 对于使用Java的用户,请注意传入类型已修改为List
- >。重置代码模板后可以看到这项改动。
思路
这个题来自打车公司lyft,现实意义是模拟该app在很多user登陆的登陆时间,narrow一个范围,这样就可以在user登陆时间最频繁的范围里投放广告或者做些其他的商业行为。
two pointer的思路。
三个指针zero、first、second同时从每个list的首元素出发。用pointerIndex来维护一个数组记录每个list当前指针的index。
比较三个指针对应的元素大小。记录比较后的curMin, curMax,更新smallest range。
移动curMin对应的指针,比较三个指针对应的元素大小。记录比较后的curMin, curMax,更新smallest range。 更新pointerIndex。
直至curMin对应的指针无法移动为止(即curMin走到了某个list的尽头)。
1 class Solution { 2 public int[] smallestRange(List> nums) { 3 int curMin = 0; 4 int curMax = Integer.MAX_VALUE; 5 int[] pointerIndex = new int[nums.size()];// maintain一个数组来记录每层list当前的pointer的index 6 boolean flag = true; // flag辅助判断是否有某个list走到了末尾 7 8 for (int i = 0; i < nums.size() && flag; i++) { // 外层循环遍历list 9 for (int j = 0; j < nums.get(i).size() && flag; j++) { // 内层循环遍历某个list的第 j 个元素 10 int minValueLevel = 0; 11 int maxValueLevel = 0; 12 for (int k = 0; k < nums.size(); k++) { 13 if (nums.get(minValueLevel).get(pointerIndex[minValueLevel]) > nums.get(k).get(pointerIndex[k])) { 14 minValueLevel = k; 15 } 16 if (nums.get(maxValueLevel).get(pointerIndex[maxValueLevel]) < nums.get(k).get(pointerIndex[k])) { 17 maxValueLevel = k; 18 } 19 } 20 // 是否更新smallest range 21 if (nums.get(maxValueLevel).get(pointerIndex[maxValueLevel]) - nums.get(minValueLevel).get(pointerIndex[minValueLevel]) < curMax - curMin) { 22 curMin = nums.get(minValueLevel).get(pointerIndex[minValueLevel]); 23 curMax = nums.get(maxValueLevel).get(pointerIndex[maxValueLevel]); 24 } 25 // 移动当前找到的最小值对应的pointer 26 pointerIndex[minValueLevel]++; 27 // flag辅助判断是否有某个list走到了末尾 28 if (pointerIndex[minValueLevel] == nums.get(minValueLevel).size()) { 29 flag = false; 30 break; 31 } 32 33 } 34 } 35 return new int[]{curMin, curMax}; 36 } 37 }
这个代码在oj上显示time limit exceeded
因为每次都要比较三个指针对应元素的curMin和curMax, 我们可以用一个PriorityQueue来优化。
PriorityQueue里面存放当前三个指针对应的元素。
PriorityQueue 删除极值的时间复杂度是 O(logN), 查找极值的时间复杂度是 O(1)
能够在时间上进行优化。
这个代码在oj上显示time limit exceeded
因为每次都要比较三个指针对应元素的curMin和curMax, 我们可以用一个PriorityQueue来优化。
PriorityQueue里面存放当前三个指针对应的元素。
PriorityQueue 删除极值的时间复杂度是 O(logN), 查找极值的时间复杂度是 O(1)
能够在时间上进行优化。
1 import java.util.List; 2 import java.util.PriorityQueue; 3 4 class Solution { 5 public int[] smallestRange(List> nums) { 6 int curMin = 0; 7 int curMax = Integer.MAX_VALUE; 8 int max = Integer.MIN_VALUE; 9 int[] pointerIndex = new int[nums.size()]; 10 boolean flag = true; 11 PriorityQueue
queue = new PriorityQueue ((i, j) -> nums.get(i).get(pointerIndex[i]) - nums.get(j).get(pointerIndex[j])); 12 for (int i = 0; i < nums.size(); i++) { 13 queue.add(i); 14 max = Math.max(max, nums.get(i).get(0)); 15 } 16 for (int i = 0; i < nums.size() && flag; i++) { 17 for (int j = 0; j < nums.get(i).size() && flag; j++) { 18 int minValueLevel = queue.poll(); 19 // 是否更新smallest range 20 if (max - nums.get(minValueLevel).get(pointerIndex[minValueLevel]) < curMax - curMin) { 21 curMin = nums.get(minValueLevel).get(pointerIndex[minValueLevel]); 22 curMax = max; 23 } 24 // 移动当前找到的最小值对应的pointer 25 pointerIndex[minValueLevel]++; 26 // flag辅助判断是否有某个list走到了末尾 27 if (pointerIndex[minValueLevel] == nums.get(minValueLevel).size()) { 28 flag = false; 29 break; 30 } 31 queue.offer(minValueLevel); 32 max = Math.max(max, nums.get(minValueLevel).get(pointerIndex[minValueLevel])); 33 34 } 35 } 36 return new int[]{curMin, curMax}; 37 } 38 }