给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。
如果数组元素个数小于 2,则返回 0。
示例 1:
输入: [3,6,9,1]
输出: 3
解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。
示例 2:
输入: [10]
输出: 0
解释: 数组元素个数小于 2,因此返回 0。
说明:
你可以假设数组中所有元素都是非负整数,且数值在 32 位有符号整数范围内。
请尝试在线性时间复杂度和空间复杂度的条件下解决此问题。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-gap
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目标的困难,但是如果不看那句线性时空还是很轻松的.:
class Solution {
public int maximumGap(int[] nums) {
//线性时空想不出来啊...
Arrays.sort(nums);
int max = 0;
for(int i = 1; i < nums.length; ++i) {
if(nums[i] - nums[i - 1] > max) {
max = nums[i] - nums[i - 1];
}
}
return max;
}
}
java的排序似乎用的是快排,所以时间是nlgn
看题解看看有什么线性时空的方法,先是基排序,这个之前在算法导论里看过,总之是先按个位排序,然后按十位排序依次类推。每一位排序的时候要求稳定,用桶排序就好。这样时间复杂度是n * d d是最高位数,空间是n + r r是基数的大小
容易得知基数越大最高位数越小…所以这个时空是可以调配的
实现:
class Solution {
static int base = 1000;//基数
int getK(int num, int k) {
//选中num的第k位 从0开始算
for(int i = 0; i < k; ++i) {
num /= base;
}
return num % base;
}
int findMaxK(int num) {
//1返回1 10返回2 类似这样
int i = 0;
while(num != 0) {
num /= base;
++i;
}
return i;
}
int[] sortBy(int[] nums, int k) {
//以最低k位排序
List[] buckets = new List[base];
for(int i = 0; i < base; ++i) {
buckets[i] = new ArrayList<Integer>();
}
for(int i : nums) {
int ii = getK(i, k);
buckets[ii].add(i);
}
int i = 0, lIndex = 0, inList = 0;
while(i < nums.length) {
while(buckets[lIndex].size() == inList) {
inList = 0;
++lIndex;
}
nums[i++] = (Integer)buckets[lIndex].get(inList++);
}
return nums;
}
int[] radixSort(int[] nums) {
int maxK = 0;
for(int i : nums) {
maxK = Math.max(maxK, findMaxK(i));
}
//System.out.println(maxK);
for(int i = 0; i < maxK; ++i) {
nums = sortBy(nums, i);
}
return nums;
}
public int maximumGap(int[] nums) {
//基排序
radixSort(nums);
int max = 0;
for(int i = 1; i < nums.length; ++i) {
if(nums[i] - nums[i - 1] > max) {
max = nums[i] - nums[i - 1];
}
}
return max;
}
}
实际算起来时间要比用java自带的排序慢了好多…还是数组元素不够多啊
题解还给了一种分桶的,思路是:
把数放到若干个桶里。如果我能保证每个桶内不存在最大间距,那么我只需要维护每个桶里的最大值和最小值,算桶间最大间距就好。
至于桶的大小,如果我能知道最大间距的最小值,把桶设的比这个值小,桶内就一定不会出现最大间距的两个数。
容易知道平分的时候最大间距最小(不平分的时候总是会有一个间距更大),所以桶的大小设成比(max - min) / (n - 1)小 桶内一定不会出现最大间距 算出上面那个数,向下取整作为桶的大小就好
实现:
class Solution {
int getMin(int[] nums) {
int min = Integer.MAX_VALUE;
for(int i : nums) {
min = Math.min(min, i);
}
return min;
}
int getMax(int[] nums) {
int max = Integer.MIN_VALUE;
for(int i : nums) {
max = Math.max(max, i);
}
return max;
}
public int maximumGap(int[] nums) {
//分桶
if(nums.length < 2) {
return 0;
}
int min = getMin(nums), max = getMax(nums);
int bucketSize = Math.max((max - min) / (nums.length - 1), 1);
if(min == max) {
return 0;
}
Integer[] mins = new Integer[(max - min) / bucketSize + 2];
Integer[] maxs = new Integer[(max - min) / bucketSize + 2];
for(int i : nums) {
//先算出应该在哪个桶
int bucketIndex = (i - min) / bucketSize;
if(mins[bucketIndex] == null || mins[bucketIndex] > i) {
mins[bucketIndex] = i;
}
if(maxs[bucketIndex] == null || maxs[bucketIndex] < i) {
maxs[bucketIndex] = i;
}
}
int l = 0, r = 0;
while(mins[l] == null) {
++l;
}
r = l;
int ret = 0;
while(l < mins.length) {
r = l + 1;
while(r < mins.length && mins[r] == null) {
++r;
}
if(r < mins.length) {
ret = Math.max(ret, mins[r] - maxs[l]);
}
l = r;
}
return ret;
}
}
有一些细节需要注意
1.bucketSize不能为0 bucketSize为1的时候其实退化成了桶排序
2.min == max的情形要特殊处理