Q:给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。
(请尝试在线性时间复杂度和空间复杂度的条件下解决此问题)
input:6 12 2 4 3 17 11 5 10
output:5
思路分析
- 先sort然后一次遍历,虽然简单直观但显然不满足O(n)
- 排序如何才能降到O(n)呢?桶排序,用空间换时间!
- 桶排序的两个经典问题:桶的宽度是多少?有多少个桶?似乎遇到了瓶颈。
深入理解
想象一下,长度为 ( m a x − m i n ) (max-min) (max−min)的直线上有N个点,假设,取最极端的情况——所有点在直线上均匀分布,即相邻两点之间的距离为 [ ( m a x − m i n ) / ( N − 1 ) ] [(max - min) / (N - 1)] [(max−min)/(N−1)]
注意!这些均匀分布点,只要有一个点不老实发生了偏移,必然会导致出现一个比上面的距离更大的相邻间距(一定是一个距离减小,另一个增大)
再看看上面的数字,显然不是老老实实等距分布的。那么,我们所要寻找的「最大间距」,必然是大于理想的「均匀间距」的
我们就取「桶的宽度」为这个理想的均匀间距,并结合长度 ( m a x − m i n ) (max-min) (max−min)就能轻易得到「桶的个数」。为什么这样取呢?
这样可以保证,最大间距一定出现在桶之间,而绝不会出现在桶内部 —— 这句话是本算法的灵魂
细节
- 桶的宽度 d = ( m a x − m i n ) / ( N − 1 ) (max - min) / (N - 1) (max−min)/(N−1) —— 向下取整,这是为了保证最大间距是大于它的
- 桶的个数 bucketNum = [ ( m a x − m i n ) / d ] + 1 [(max - min) / d] + 1 [(max−min)/d]+1 —— 多取1,这是保证最大值也有桶可放
- 桶的编号 index = ( n u m s [ i ] − m i n ) / d (nums[i] - min) / d (nums[i]−min)/d
- 如何计算桶之间的间距?这是通过维护每个桶中的最大/最小值实现的。
举个栗子
9个数字,已排序 —— 1, 3, 4, 5, 6, 10, 11, 12, 17
桶的宽度 d = 2
桶的个数 bucket = 9
[1, 3) | [3, 5) | [5, 7) | [7, 9) | [9, 11) | [11, 13) | [13, 15) | [15, 17) | [17, 19) |
---|---|---|---|---|---|---|---|---|
1 | 3, 4 | 5, 6 | 10 | 11, 12 | 17 |
最大间距 maxGap = 17 - 12 = 5
代码
class Solution {
public int maximumGap(int[] nums) {
// 数组长度小于2直接 return 0
int len = nums.length;
if(len < 2){
return 0;
}
int min = Arrays.stream(nums).min().getAsInt();
int max = Arrays.stream(nums).max().getAsInt();
int d = Math.max(1, (max - min) / (len - 1)); // 桶的宽度
int bucketNum = (max - min) / d + 1; // 桶的个数
// 初始化桶
int[][] bucket = new int[bucketNum][2];
for(int[] arr : bucket){
Arrays.fill(arr, -1); // (-1, -1)表示空桶
}
// 向桶中装填数字,维护每个桶中的最大/最小值
for(int i = 0; i < len; i++){
int index = (nums[i] - min) / d;
if(bucket[index][0] == -1){
bucket[index][0] = nums[i];
bucket[index][1] = nums[i];
}else{
bucket[index][0] = Math.min(bucket[index][0], nums[i]);
bucket[index][1] = Math.max(bucket[index][1], nums[i]);
}
}
// 遍历桶
// 后一个桶的最小值 - 前一个桶的最大值,尝试更新res
int res = 0;
int pre = -1;
for(int index = 0; index < bucket.length; index++){
if(bucket[index][0] == -1){
continue;
}
if(pre != -1){
res = Math.max(res, bucket[index][0] - bucket[pre][1]);
}
pre = index;
}
return res;
}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)
- 桶排序的O(n)…称不上严谨,但理论上来说的确是O(n)的数量级 >_<
E N D END END