Date:
Nov. 6, 2017
Problem:
https://leetcode.com/problems/smallest-range/description/
Description:
You have k lists of sorted integers in ascending order. Find the smallest range that includes at least one number from each of the k lists.
We define the range [a,b] is smaller than range [c,d] if b-a < d-c or a < c if b-a == d-c.
Example 1:
Input:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
Output: [20,24]
Explanation:
List 1: [4, 10, 15, 24,26], 24 is in range [20,24].
List 2: [0, 9, 12, 20], 20 is in range [20,24].
List 3: [5, 18, 22, 30], 22 is in range [20,24].
Note:
The given list may contain duplicates, so ascending order means >= here.
1 <= k <= 3500
-10^5 <= value of elements <= 10^5.
For Java users, please note that the input type has been changed to List<List<Integer>>. And after you reset the code template, you'll see this point.
寻找一个range,包含所有list中至少一个元素,换言之,我们只关心range包含了所有list中的一个元素。
考虑我们已经有这样一个候选的range,它对应一个队列,这个队列由所有list中的某一个元素组成,对example1而言,它可能是[0, 4, 5],其中0来自list 2,4来自list 1,5来自list3。这时我们可以计算队列的range大小,即max(list) - min(list),这个range大小的具体计算方式并不重要,我们只需要一个稳定可靠的参考数据,让我们区分哪个队列的range更符合题意。
这时候我们有了一个队列,它包含了每个list中的一个元素,给出了一个range参考值,我们可以维护一个最大参考值和它对应的数据来储存当前最符合题意的range。我们希望制造一个新的队列,来寻找更小的range参考值。这时,可以证明,最有意义的队列更新方式是将最小元素出队,将最小元素所在的list中的一个更大的元素入队(我们很自然地会让最小元素之后的那个元素入队,这是简单而符合直觉的遍历方式)。如果我们将最小元素出队,我们可能获得一个在range中或者离range不远的新元素,来使得range参考值变得更小,也有可能获得一个离range十万八千里的新元素,这时候我们不会更新最大参考值。但是,如果我们不将最小元素出队,队列的下界一定不会发生变化,甚至队列的上界还有可能增加。因此,只要我们希望寻找更小的range参考值,我们要做的事情就是令最小元素出队,读取其所在队列的下一个元素(当然,这是我们从左向右扫描时的直观思路,我们也可以倒着来,不过那没什么意义)。
于是,我们维护一个红黑树来储存这些元素,记录并维护队列最佳上界下界(range参考值最小),按上述方式扫描所有list,直到其中一个list在末端触发了出队和向后扫描。这时,我们不得不结束扫描并且返回最佳上下界,因为我们无法再找到可能使range参考值更小且包含所有list中至少一个元素的队列了。
这个算法的复杂度我不会算(。
大概是O(M*N),M为list平均长度,N为list数。
以下是submission。
1 class Solution 2 { 3 public: 4 vector<int> smallestRange(vector<vector<int>>& nums) 5 { 6 vector<int> ans(2); 7 multiset<int> kinity; 8 int k = nums.size(); 9 vector<int> loc(k, 0); 10 vector<int> len(k, 0); 11 for (int i = 0; i < k; i++) 12 len[i] = nums[i].size() - 1; 13 for (int i = 0; i < k; i++) 14 kinity.insert(nums[i][0]); 15 ans[0] = *(kinity.begin()); 16 ans[1] = *(--kinity.end()); 17 cout << ans[0] << ' ' << ans[1] << endl; 18 for (auto i = kinity.begin(); i != kinity.end(); i++) 19 cout << *i << ' '; 20 cout << endl; 21 while (1) 22 { 23 for (int i = 0; i < k; i++) 24 if (nums[i][loc[i]] == *(kinity.begin())) 25 { 26 if (loc[i] == len[i]) 27 return ans; 28 kinity.erase(kinity.begin()); 29 loc[i]++; 30 kinity.insert(nums[i][loc[i]]); 31 if (ans[1] - ans[0] > *(--kinity.end()) - *(kinity.begin()) 32 or ans[1] - ans[0] == *(--kinity.end()) - *(kinity.begin()) 33 and ans[0] > *(kinity.begin())) 34 { 35 ans[0] = *(kinity.begin()); 36 ans[1] = *(--kinity.end()); 37 } 38 for (auto i = kinity.begin(); i != kinity.end(); i++) 39 cout << *i << ' '; 40 cout << endl; 41 cout << ans[0] << ' ' << ans[1] << endl; 42 break; 43 } 44 } 45 } 46 };