桶排序
问题:
给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。
如果数组元素个数小于 2,则返回 0。
示例 1:
输入: [3,6,9,1]
输出: 3
解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。
示例 2:
输入: [10]
输出: 0
解释: 数组元素个数小于 2,因此返回 0。
说明:
你可以假设数组中所有元素都是非负整数,且数值在 32 位有符号整数范围内。
请尝试在线性时间复杂度和空间复杂度的条件下解决此问题。
思路:
需要时间复杂度为O(n)的排序算法
桶排序:
我们设置n个桶,每个桶有相同的容量,存储一定范围的数字,通过设置桶的容量,我们得到这n个桶,随后遍历数组,将每个数字放到对应的桶中,再对每个桶进行排序,随后遍历所有桶,将所有桶中元素按顺序连接,就得到了最后的排序数组。当我们设置每个桶中仅保存一个1个元素时,桶排序的最好情况下的时间复杂度可以达到O(n)。
比如我们要对数组 { 4 , 2 , 6 , 8 , 9 , 5 , 7 , 3 , 1 } \{4,2,6,8,9,5,7,3,1\} {4,2,6,8,9,5,7,3,1}排序,首先我们遍历一遍数组,得到最大值9和最小值1,因此我们设置9个桶,分别存放1~9。遍历数组,将对应的数字放到对应的桶中。按桶的顺序遍历桶,就得到了排好序的数组 { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } \{1,2,3,4,5,6,7,8,9\} {1,2,3,4,5,6,7,8,9}。
回到这个问题
考虑元素之间的间距,显然,当所有元素的间距相等时,我们能够得到最小的最大间距。我们设数组中有
n
n
n个元素,则共有
(
n
−
1
)
(n-1)
(n−1)个间隔;设数组中的最大值为
m
a
x
max
max,最小值为
m
i
n
min
min,那么我们可以得到,当所有元素的间距相等时,间距
t
t
t为
m
a
x
−
m
i
n
n
−
1
\frac{max -min}{n-1}
n−1max−min
考虑刚才的桶排序,如果我们将桶的容量
b
b
b设为
b
=
t
=
m
a
x
−
m
i
n
n
−
1
b=t=\frac{max-min}{n-1}
b=t=n−1max−min,因为桶的容量即为最小的间距,则最大间距一定出现在两个桶之间,这样设计,我们就仅需比较两个桶的间距就可以了,大大减小了时间复杂度。
同时,因为我们只需比较两个桶之间的间距,不关注桶内的间距,因此每个桶可以只存储一个最大值和一个最小值。
复杂度分析
n n n为数组长度, N N N为桶个数
时间复杂度: O ( 2 n + N ) ∼ O ( n ) O(2n + N)\sim O(n) O(2n+N)∼O(n),遍历两次数组,一次寻找最大最小值,一次将元素放入桶中,遍历一次桶
空间复杂度: O ( 2 N ) ∼ O ( N ) O(2N) \sim O(N) O(2N)∼O(N),每个桶存储两个数
实现:
class Solution {
public int maximumGap(int[] nums) {
if(nums.length < 2)
return 0;
int min_val = Integer.MAX_VALUE;
int max_val = 0;
//寻找最大值和最小值
for(int i : nums) {
if(i > max_val)
max_val = i;
if(i < min_val)
min_val = i;
}
//初始化桶,用-1表示桶中没有元素
int bucket_size = Math.max(1, (max_val - min_val) / (nums.length - 1));
int[][] bucket = new int[(max_val - min_val) / bucket_size + 1][2];
for(int i = 0; i < bucket.length; i++) {
bucket[i][0] = -1;
bucket[i][1] = -1;
}
//遍历元素放入相应的桶
for(int i : nums) {
int idx = (i - min_val) / bucket_size;
if(bucket[idx][0] == -1 || bucket[idx][0] > i)
bucket[idx][0] = i;
if(bucket[idx][1] == -1 || bucket[idx][1] < i)
bucket[idx][1] = i;
}
int res = 0;
int l = bucket[0][1];
//遍历桶找到最大间距
for(int i = 1; i < bucket.length; i++) {
while(bucket[i][0] == -1)
i++;
res = Math.max(res, bucket[i][0] - l);
l = bucket[i][1];
}
return res;
}
}
桶排序的关键其实是如何确定桶的大小,如何在某些问题中应用桶排序。