[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;
    }
};

原文地址:https://www.cnblogs.com/r1FusR/p/12431553.html

时间: 2024-07-31 18:48:22

[LeetCode] 632. Smallest Range Covering Elements from K Lists的相关文章

[LeetCode] 910. Smallest Range II 最小区间之二

Given an array?A?of integers, for each integer?A[i]?we need to choose?either?x = -K?or?x = K, and add?x?to?A[i]?(only once). After this process, we have some array?B. Return the smallest possible difference between the maximum value of?B?and the mini

lc 632. Smallest Range

https://leetcode.com/problems/smallest-range/description/ 给你k个数组,找一个最小区间[a,b],可以包含k个数组中的数字各至少一个. 滑动窗口题. 对于要求"最短"的题目很适用. points: 1.在扩张右界的时候,一旦碰到合法就停止,但不用记录结果.在收缩左界的时候进行记录(判断). code: import heapq class Solution: def __init__(self): self.a=None sel

LeetCode 910. Smallest Range II

很有意思的一道数学推理题目, 剪枝以后解法也很简洁.初看貌似需要把每个数跟其他数作比较.但排序以后可以发现情况大大简化:对于任一对元素a[i] < a[j], a[i] - k和a[j] + k 的情况可以排除, 因为会产生比原值更大的差, 所以对于原有数组的最小值min最大值max, (min - k, max + k)的情况可以排除.剩下的三种情况, (min - k, max - k), (min + k, max + k) 和 (min + k, max - k),后两种等价于原值max

一道题目- Find the smallest range that includes at least one number from each of the k lists

You have k lists of sorted integers. Find the smallest range that includes at least one number from each of the k lists. For example, List 1: [4, 10, 15, 24, 26] List 2: [0, 9, 12, 20] List 3: [5, 18, 22, 30] The smallest range here would be [20, 24]

[LeetCode] Kth Smallest Number in Multiplication Table 乘法表中的第K小的数字

Nearly every one have used the Multiplication Table. But could you find out the k-th smallest number quickly from the multiplication table? Given the height m and the length n of a m * n Multiplication Table, and a positive integer k, you need to ret

Leetcode 632.最小区间

最小区间 你有 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

leetcode 刷题之路 93 Merge k Sorted Lists

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 将k个有序链表合并成一个有序链表. 思路,之前做过两个有序链表的合并操作,对于k个链表,我们最先想到的就是能不能转化为我们熟悉的两个链表的合并,可以我们先将k个链表分为两部分,先对这两部分完成链表的有序合并操作,再对合并得到的两个链表进行合并,这是一个递归性质的描述,采用递归很容易实现这个程序,和数组

LeetCode Search for Range

class Solution { private: int getDirection(int A[], int idx, int target, bool isDefaultBack) { int r = A[idx] - target; if (r == 0) { r = isDefaultBack ? -1 : 1; } return r; } int getFirstValueIndex(int A[], int n, int target, bool isFromBack) { int

LeetCode:乘法表中的第K小的数【668】

LeetCode:乘法表中的第K小的数[668] 题目描述 几乎每一个人都用 乘法表.但是你能在乘法表中快速找到第k小的数字吗? 给定高度m .宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字. 例 1: 输入: m = 3, n = 3, k = 5 输出: 3 解释: 乘法表: 1 2 3 2 4 6 3 6 9 第5小的数字是 3 (1, 2, 2, 3, 3). 例 2: 输入: m = 2, n = 3, k = 6 输出: 6 解释: 乘法表: 1 2