你有 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] 中。
来源:力扣(LeetCode)第632题
双指针
双指针是一种解决问题的技巧或者思维方式,指在访问一个序列中的数据时使用两个指针进行扫描,两个指针可以是同向的,也可以是反向的;我们的关注点可以是这两个指针指向的两个元素本身,也可以是两个指针中间的区域。
滑动窗口
窗口代表着数组或者序列上的一组元素,用 left 和 right 两个指针分别表示其左右边界,从左到右移动 left 和 right 指针,就像是一个窗口在序列上进行滑动,不断的有新元素从右侧进入窗口,同时有旧的元素从左侧移除窗口,当前窗口内的元素是否满足需求制约着窗口的扩张与收缩。滑动窗口可以用来解决子串问题。
滑动窗口基本原理
- 窗口初始化
设置窗口初始化值为 0 ,即 left 和 right 都位于序列的左端位置。 - 窗口扩张
窗口扩张时 left 指针保持不动,向右移动 right 指针;在窗口扩张的初期,窗口右侧纳入新的元素对结果的影响是积极的,有以下两种情况:
①.新加入的元素使窗口得到的结果更优,则每一步都是一个当前最优解;停止扩张的临界条件是新加入的元素刚好使窗口不符合条件。
②.新加入的元素使窗口离达到目标条件更近;停止扩展的临界条件是新加入的元素刚好使窗口符合目标条件。
当窗口达到停止扩张的临界条件时,继续扩张对结果的影响是消极的,此时应该进行窗口收缩。 - 窗口收缩
窗口收缩时 right 指针保持不动,向右移动 left 指针;窗口收缩的初期,窗口左侧丢弃的元素对结果的影响的积极的,有以下两种情况:
①.若窗口停止扩张的临界条件是新加入的元素刚好使窗口不符合目标条件,则丢弃一个旧元素可能使窗口满足条件,停止收缩的临界条件是丢弃一个元素使窗口刚好满足目标条件。
②.若窗口停止扩张的临界条件是新加入的元素刚好使窗口符合目标条件,则丢弃一个旧元素可能使窗口的结果更优,停止收缩的临界条件是丢弃一个元素使窗口刚好不满足目标条件。
当窗口达到停止收缩的临界条件时,继续收缩对结果的影响是消极的,此时应该进行窗口扩张。 - 滑动停止
滑动窗口的过程就是不断的进行窗口扩张与收缩,当窗口扩张对结果的影响是积极时就进行窗口扩张,当窗口收缩对结果的影响时积极时就进行窗口收缩,不断重复这个过程。当right 指针到达序列末尾时,窗口滑动停止。
题目分析
将所有数组的元素合并成一组后进行升序排列,本题即转换为在新数组上找一个区间能包含各个组的元素即可,因此可以用滑动窗口求解
代码示例
class Solution {
public:
vector<int> smallestRange(vector<vector<int>>& nums) {
multimap<int,int> numstomap ;//元素值-所属组序号,默认按值升序
for(int i = 0;i < nums.size();i ++ )
{
for(int j = 0;j < nums[i].size();j++)
{
numstomap.insert(pair<int,int>(nums[i][j],i));
}
}
multimap<int,int>::iterator left = numstomap.begin();//左指针,左右指针初始都位于左侧
multimap<int,int>::iterator right = numstomap.begin();//右指针
int res = INT_MAX;//设置一个绝对最大初始值
int leftv = 0;//返回结果的两个元素值
int rightv= 0;//
int k = nums.size();//组个数
unordered_map<int,int> curmap;//组序号-个数
while( right != numstomap.end() )//终止条件
{
curmap[right->second] ++;//窗口扩张纳入的新元素
while(curmap.size() == k )//已经找到了一个可行解、优化它
{
if( right->first - left->first < res)//记录可行解
{
res = right->first - left->first;
leftv = left->first;
rightv = right->first;
}
curmap[left->second] --;//收缩窗口
if( curmap[left->second] == 0)
{
curmap.erase(left->second);
}
left++;
}
right++;
}
if( res == INT_MAX ) return {};
else return {leftv,rightv};
}
};