Leetcode刷题笔记 632. 最小区间

632. 最小区间


时间:2020年8月1日
知识点:滑动窗口
题目链接: https://leetcode-cn.com/problems/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] 中。

注意
给定的列表可能包含重复元素,所以在这里升序表示 >= 。
1 <= k <= 3500
-105 <= 元素的值 <= 105
解法

  1. 我们把nums中的元素放入一个数组中,并标记元素来自哪个列表,可以得到
 nums中所有元素排序:0 4 5 9 10 12 15 18 20 22 24 26 30
    元素来自哪个列表:1 0 2 1 0  1  0  2  1  2  0  0  2
  1. 要求找到一个最小区间,使得每个列表中至少有一个数包含在其中,也就是找到一个区间[a,b],a、b两个元素之间要包含 所有 不同列表的值(即0、1、2)
  2. 当 a=0,b=5时,这个区间包含来自第1、0、2列列表的值,是其中一个区间(不是最小的);而当a=10,b=15时,其中只包含来自0、1列表值,不是一个区间;当a=15,b=22时,也包含第0、2、1、2个列表中的值。
  3. 可以用滑动窗口来解决,这些窗口中要包含所有列(0,1,2)
  4. 用 left 和 right 两个指针分别表示其左右边界,从左到右移动 left 和 right 指针,就像是一个窗口在序列上进行滑动,不断的有新元素从右侧进入窗口,同时有旧的元素从左侧移除窗口,当前窗口内的元素是否满足需求制约着窗口的扩张与收缩。

代码

#include <stdio.h>
#include <vector>
#include <iostream>
#include <unordered_map>
using namespace std;
class Solution {
public:
    vector<int> smallestRange(vector<vector<int>>& nums) {
        //放入数组中,并排序
        int k = nums.size();//k个列表
        vector<pair<int, int>> map;
        for (int i=0;i<k;i++){
            for(int j=0;j<nums[i].size();j++){
                map.push_back({nums[i][j],i});
            }
        }
        sort(map.begin(),map.end());
        
        vector<int> flag(k);//标记区间中是否有k个列表中的元素
        vector<int> ans;
        int left = 0,n= map.size(),cnt=0;
        int dif = INT_MAX;
        //往右滑动窗口
        for (int right = 0; right < n; right ++) {
            //判断这个元素来自哪个列表,如第一次出现,标记在[left,right]区间中,这个列表中出现过元素了。
            if (flag[map[right].second] == 0) cnt ++;
            flag[map[right].second] ++;
            //k个不同的列表中,每个列表中至少有一个元素在区间出现过了,寻找最小区间
            while (cnt == k && left <= right) {
                if (dif > map[right].first - map[left].first) {
                    dif = map[right].first - map[left].first;
                    ans = {map[left].first, map[right].first};
                }
                //向左移动
                flag[map[left].second]--;
                if(flag[map[left].second] == 0)
                    cnt--;
                left++;
            }
        }
        return ans;
    }
};
int main()
{
    vector<vector<int>> d(3, vector<int> ());
    d[0] = {4,10,15,24,26};
    d[1] = {0,9,12,20};
    d[2] = {5,18,22,30};
    Solution s;
    vector<int> ans = s.smallestRange(d);
    cout<<ans[0]<<" "<<ans[1];
    return 0;
}

今天也是爱zz的一天哦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值