632. Smallest Range Covering Elements from K Lists

632. 最小区间

Posted by bbkgl on August 2, 2020

解把飞花蒙日月

不知天地有清霜

每日一题连续hard,让人有点难受呀。

大部分情况下,hard我都搞不出来呀,思路也是看了大佬的。

思路大概是这样:

  • 首先把k个数组合并成一个数组
    • 数组的每个元素都是一个结构体
    • 结构体包含元素的值以及元素原来所在的数组的索引
  • 将合并的数组进行排序
  • 在该有序数组中查找一个区间,该区间满足
    • 包含了每个原数组的至少一个索引
    • 元素的值区间最小

既然是在有序数组中寻找一个区间,就考虑滑动窗口/双指针。

窗口从左到右移动,并记录窗口中存在的原数组索引个数 cnt

  • 如果 cnt 小于原数组个数,则左边界右移
  • 反之则左移

移动过程中维护一些变量。

class Solution {
private:
    static constexpr int RANGE = 1e5;

    static bool cmp(pair<int, int> a, pair<int, int> b) {
        if (a.first == b.first)
            return a.second < b.second;
        else return a.first < b.first;
    }
public:
    vector<int> smallestRange(vector<vector<int>>& nums) {
        int n = nums.size();
        vector<pair<int, int>> all;
        for (int i = 0; i < n; i++) {
            for (const int &it : nums[i]) {
                all.emplace_back(make_pair(it, i));
            }
        }
        int m = all.size();
        sort(all.begin(), all.end(), cmp);
        vector<int> hash(n);
        hash[all[0].second]++;
        int cnt = 1, right = 0, left = 0, ans_left = -RANGE, ans_right = RANGE;
        while (left <= right) {
            if (cnt < n && right < m - 1) {
                right++;
                if (hash[all[right].second] == 0)
                    cnt++;
                hash[all[right].second]++;
            } else {
                if (cnt >= n && (ans_right - ans_left > all[right].first - all[left].first)) {
                    ans_right = all[right].first;
                    ans_left = all[left].first;
                }
                hash[all[left].second]--;
                if (hash[all[left].second] == 0)
                    cnt--;
                left++;
            }
        }
        vector<int> ans = {ans_left, ans_right};
        return ans;
    }
};