632. 最小区间
你有 k 个升序排列的整数数组。找到一个最小区间,使得 k 个列表中的每个列表至少有一个数包含在其中。
我们定义如果 b-a < d-c 或者在 b-a == d-c 时 a < c,则区间 [a,b] 比 [c,d] 小。
思路
堆
问题转化为从k个列表中各取一个数字,保证这k个数的最大值和最小值之差最小。
使用一个最小堆维护当前k个指针指向元素的最小值,同时维护堆元素的最大值,每次从堆中取出最小值,根据最大值和最小值计算当前区间,如果当前区间小于最小区间则用当前区间更新最小区间,然后将对应列表的指针右移,将新元素加入堆中,并更新堆中元素的最大值。
代码
//重载仿函数,维护最小堆
struct cmp {
bool operator() (const pair<int, int>& a1, const pair<int, int>& a2) {
if(a1.second == a2.second) {
return a1.first > a2.first;
}
return a1.second > a2.second;
}
};
class Solution {
public:
vector<int> smallestRange(vector<vector<int>>& nums) {
int leftRange = 0, rightRange = INT_MAX;
int size = nums.size();
priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> pq;
//维护k个列表的访问位置
vector<int> next(size, 0);
int minValue = 0, maxValue = INT_MIN;
//初始状态为k个列表的0号元素
for(int i = 0; i < size; i++) {
pq.push({i, nums[i][0]});
maxValue = max(maxValue, nums[i][0]);
}
while(true) {
pair<int, int> tmp = pq.top();
pq.pop();
//取出最小值
minValue = tmp.second;
//更新区间
if(maxValue - minValue < rightRange - leftRange) {
leftRange = minValue;
rightRange = maxValue;
}
//一个列表遍历完毕
if(next[tmp.first] == nums[tmp.first].size() - 1){
break;
}
//最小值列表所在元素位置前进
++next[tmp.first];
//新加入元素,最大值需重新维护
maxValue = max(maxValue, nums[tmp.first][next[tmp.first]]);
//将新元素加入堆
pq.push({tmp.first, nums[tmp.first][next[tmp.first]]});
}
return {leftRange, rightRange};
}
};