[LeetCode] 632. Smallest Range Covering Elements from K Lists
Posted r1fusr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LeetCode] 632. Smallest Range Covering Elements from K Lists相关的知识,希望对你有一定的参考价值。
[LeetCode]632. Smallest Range Covering Elements from K Lists
你有 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] 中
这道题当时拿到的时候想:要找一个包含三个的列表中的某一个数的最小区间,可以把三个列表中的一个数拿出来,形成一个类似滑动窗口的数组,保持每组取一个,在不断滑动的过程中找到使得(最大值-最小值)取到最小的一组便是答案。便选择建立一个迭代器列表,每次进行sort排序来获取最大最小值,之后迭代器++来实现滑动。
首先第一个坑就是用了迭代器建立列表,在比较的时候有用了begin()和back()来挑选最大值和最小值,指针一顿乱指,都给我指懵逼了。例如:
vector<int> a; vector<vector<int>::iterator>> b; // // int t1 = a.begin(); a.begin()是一个迭代器,不能直接赋值 int t1 = *(a.begin()); // int t2 = b.begin(); // int t2 = *(b.begin()); b.begin()是一个指向迭代器的迭代器,需要两个*来指到int int t2 = **(b.begin()); int t3 = a.back(); // a.back()返回的是元素本身,而非指针
第二个坑就是超时问题,首先想到的是用遍历来替代掉sort,把时间复杂度从Onlogn降到On,而最终的速度还是很慢,只有2%左右。然后通过大神同学的提醒学习了堆的用法,虽然用起来确实很简单但是毕竟第一次,踩了一个大坑:
vector<int> vi; // make_heap可以使得vi成为一个堆!只满足堆的性质而没有排序! make_heap(vi.begin(), vi.end(), cmp); // 以下操作只有在vi确定是一个堆的情况下才能进行! // pop_heap可以把vi的堆顶元素移到末尾,而不做其他操作! pop_heap(vi.begin(), vi.end(), cmp); // 如果真的要删掉堆顶又保持堆: // pop_heap(vi.begin(), vi.end(), cmp); // vi.pop_back(); // push_heap可以把vi的末尾新插入元素进行排序而形成一个堆,与make_heap相似 push_heap(vi.begin(), vi.end(), cmp); // 插入实际上可以如下操作: // vi.push_back(); // push_heap(vi.begin(), vi.end(), cmp); // sort_heap只有在vi是一个堆的前提下才能将整个堆像sort一样排序! sort_heap(vi.begin(), vi.end(), cmp);
简单来说就是必须是堆才能用sort_heap排序!!!由于整个算法中可以直接利用迭代器自加来更新,无需插入与删除就可以原地变换,我便直接用sort来进行排序结果出了错。其实可以在堆顶元素进行自加之后通过pop_heap()将元素放到末尾,之后用push_heap重新构成堆,这样的时间复杂度从O(n)下降到了O(logn)。最终打败50%左右,虽然不是太好,但也算是学到不少,学到就是赚到。
class Solution { public: static bool cmp(vector<int>::iterator a,vector<int>::iterator b){ return *a > *b; } vector<int> smallestRange(vector<vector<int>>& nums) { int mint = INT_MAX; vector<int> ans; vector<vector<int>::iterator> m; // 保存每个vector的值 set<vector<int>::iterator> st; // 保存每个vector的vi.end() for(int i=0; i<nums.size(); ++i){ vector<int>::iterator it = nums[i].begin(); st.insert(nums[i].end()); m.push_back(it); } make_heap(m.begin(), m.end(), cmp); // 用堆保存当前维护的一组 int maxs = INT_MIN; for(auto i:m){ if(*i>maxs) maxs = *i; } auto mins = m.begin(); while(true){ int tmp = maxs - **mins; if(tmp < mint){ ans.clear(); ans.push_back(**mins); ans.push_back(maxs); mint = tmp; } (*mins)++; if(st.count(*(m.begin()))){ // 判断是否到达数组末尾 break; } if(**mins > maxs){ // 判断新插入元素与最大值的关系 maxs = **mins; } pop_heap(m.begin(), m.end(), cmp); push_heap(m.begin(), m.end(), cmp); // 通过pop_heap与push_heap来完成重新排序 mins = m.begin(); } return ans; } };
以上是关于[LeetCode] 632. Smallest Range Covering Elements from K Lists的主要内容,如果未能解决你的问题,请参考以下文章
codeforces 632C. The Smallest String Concatenation 排序
LeetCode 483. Smallest Good Base