题目来源
题目描述
class Solution {
public:
int maximumGap(vector<int>& nums) {
}
};
题目解析
桶排序
借助桶排序的思想,假设答案法。
整个数组假设一共有9个数,其最小值是0,最大值是99。
那么我们将整个范围[0,99]10等分。
- 为什么是10等分,因为一共有9个数
- 如果有10个数,那么就将[0,99]11等分
- 如果有11个数,那么就将[0,99]12等分
- …
- 如果有N个数,那么就把从[最小值,最大值]一共N+1等分
从而得到了10个桶:
- 第0个桶只收集范围在
0~10
区间的数 - 第1个桶只收集范围在
11~21
区间的数 - 第2个桶只收集范围在
22~32
区间的数 - 第3个桶只收集范围在
33~43
区间的数 - 第4个桶只收集范围在
44~54
区间的数 - …
- 最后一个空只收集
99
因此:
- 最小值一定会进最左边的桶,最大值一定会近最右边的桶
- (所以空桶一定不会出现在左右两侧)
- 可能会存在多个空桶,空桶的边上可能是空桶
- 一共有10个桶,但是只有9个数,所以中间一定会有一个空桶
如果拍完序之后: 相邻两数就可能来自同一个桶,也可能来自跨桶的
- 空桶的左右两侧一定存在非空桶:
- 左侧的非空桶的最大值,和左侧的非空桶的最小值一定相邻,而且它们之间的差值一定大于桶范围
- 来自同一个桶的数一定不是我们要找的。因为同一个桶的相邻数减完之后差值不会大于桶区间;
- 这就排除了一大票可能的数,因此每个桶我们只需要维护其最大值和最小值即可,完全不需要考虑桶内的数据是怎么分布的
- 最大值一定来自空桶两侧吗?不一定
问题:怎么决定nums[i]应该进哪个桶?
int bucket(int num, int len, int min, int max){
return (int) ((num - min) * len / (max - min));
}
怎么分,不重要,关键是桶排序
举个例子
实现
class Solution {
int bucket(long num, long len, long min, long max) {
return (int) ((num - min) * len / (max - min));
}
public:
int maximumGap(vector<int>& nums) {
if(nums.size() < 2){
return 0;
}
int len = nums.size();
int min = INT32_MAX, max = INT32_MIN;
for (int i = 0; i < len; ++i) {
min = std::min(min, nums[i]);
max = std::max(max, nums[i]);
}
if(max == min){
return 0;
}
int bucketSize = len + 1;
std::vector<bool> hasNum(len + 1, false);
std::vector<int> maxs(len + 1, INT32_MIN);
std::vector<int> mins(len + 1, INT32_MAX);
int bid = 0;
for (int i = 0; i < len; i++) {
bid = bucket(nums[i], bucketSize - 1 , min, max);
mins[bid] = hasNum[bid] ? std::min(mins[bid], nums[i]) : nums[i];
maxs[bid] = hasNum[bid] ? std::max(maxs[bid], nums[i]) : nums[i];
hasNum[bid] = true;
}
int ans = INT32_MIN;
int lasMax = maxs[0]; // 左右两侧一定不是空桶
for (int i = 1; i <= len; ++i) {
if(hasNum[i]){
ans = std::max(ans, mins[i] - lasMax); //相邻的数
lasMax = maxs[i];
}
}
return ans;
}
};
桶排序