632. 最小区间(C++)---排序 + 滑动窗口 解题

题目详情

你有 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. 给定的列表可能包含重复元素,所以在这里升序表示 >= 。
  2. 1 <= k <= 3500
  3. -105 <= 元素的值 <= 105
  4. 对于使用Java的用户,请注意传入类型已修改为List>。重置代码模板后可以看到这项改动。

 

——题目难度:困难


 





分析

首先把 nums 里的所有元素按 (number, group) 放进 ordered (vector> ordered) 里,然后对 ordered 进行排序。

一开始有点对 "C++ sort对vector>排序问题" 感到疑惑,后面找了资料发现 std::sort 在未指定比较方法时会使用 operator< 来比较元素,而 std::pair::operator< 按标准规定会在两个 std::pair 的第一个元素互不小于对方的情况下比较第二个元素(就是 _Right.first == _Left.first 的情况下,才比较第二个元素)。

以下代码来自 VS2017 RC 的 utility 文件:

template inline
	constexpr bool operator<(const pair<_Ty1, _Ty2>& _Left,
		const pair<_Ty1, _Ty2>& _Right)
	{	// test if _Left < _Right for pairs
	return (_Left.first < _Right.first ||
		(!(_Right.first < _Left.first) && _Left.second < _Right.second));
	}

——资料来源:C++ sort对vector>排序问题

接着保证 ordered 在 [i, j] 里都有 nums.size() 个列表的至少一个元素,然后不断缩小区间,比如 只看所属组的的话,nums.size() = 3, 如果遇到 [1, 1, 2, 0] ,那么可以缩小成 [1, 2, 0];但如果遇到 [2, 1, 0, 1] ,那就不能缩小区间,因为区间得包括 nums.size() 个列表的至少一个元素,这样以此来得到最小区间。

 更新 ans 的话 只需要 当满足 ans[1] - ans[0] > ordered[j].first - ordered[i].first 即可更新,这里不用考虑 当 "b-a == d-c 时 a < c,则区间 [a,b] 比 [c,d] 小" 的情况,假如 ans = {a, b} 了, 且 d >= c  >= b >= a,那么当 {c, d} 也符合条件,但 d - c == b - a,那就看 c 和 a,因为 i ,j 是从下标 0 开始往后遍历的,所以 c >= a:当 c == a,显然说明 d == b,那就说明 两个区间相同;如果 c > a,那么说明区间 [a,b] 比 [c,d] 小,那也没必要更新。
综上所述,更新 ans 的话 只需要 当满足 ans[1] - ans[0] > ordered[j].first - ordered[i].first 即可更新。


-解题代码如下

class Solution {
public:
    vector smallestRange(vector>& nums) {
		vector> ordered; // (number, group)
		for(int i = 0; i < nums.size(); i++) {
			for(int num : nums[i]) {
				ordered.push_back({num, i});
			}
		}
		sort(ordered.begin(), ordered.end());
		
		int i = 0, k = 0; // i 为左边界 
		vector ans;
		unordered_map count;
		for(int j = 0; j < ordered.size(); j++) { // j 为右边界
			if (!count[ordered[j].second]++) k++;
			
			if (k == nums.size()) {
				while (count[ordered[i].second] > 1) count[ordered[i++].second]--;
				if (ans.empty() || ans[1] - ans[0] > ordered[j].first - ordered[i].first) {
					ans = vector{ordered[i].first, ordered[j].first};
				}
			}
		}
			
		return ans;			
    }
};

结果
632. 最小区间(C++)---排序 + 滑动窗口 解题_第1张图片

你可能感兴趣的:(LeetCode-解题记录)