[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的主要内容,如果未能解决你的问题,请参考以下文章

lc 632. Smallest Range

codeforces 632C. The Smallest String Concatenation 排序

Leetcode 632.最小区间

LeetCode 483. Smallest Good Base

908. Smallest Range I - LeetCode

Leetcode 230. Kth Smallest Element in a BST