164. Maximum Gap
Given an unsorted array, find the maximum difference between the successive elements in its sorted form.
Return 0 if the array contains less than 2 elements.
Example 1:
Input: [3,6,9,1]
Output: 3
Explanation: The sorted form of the array is [1,3,6,9], either (3,6) or (6,9) has the maximum difference 3.
Example 2:
Input: [10]
Output: 0
Explanation: The array contains less than 2 elements, therefore return 0.
Note:
You may assume all elements in the array are non-negative integers and fit in the 32-bit signed integer range.
Try to solve it in linear time/space.
solution 1: sorting
将数组排序,然后遍历求相邻元素的差值,最后得到最大gap。
public int maximumGap(int[] nums) {
if (nums == null || nums.length < 2) return 0;
int res = 0;
int len = nums.length;
Arrays.sort(nums);
for (int i = 1; i < len; i++) {
res = Math.max(res, nums[i] - nums[i-1]);
}
return res;
}
复杂度为: O ( n l o g n ) O(nlogn) O(nlogn)
solution 2: Radix sort
代替上面的Arrays.sort(),使用radix sort(bucket sort)进行排序,采用LSD。radix sort有两种方法LSD(Least sgnificant digital),MSD(Most sgnificant digital),LSD由键值的最右边(个位)开始排序,MSD则相反,LSD适用于digits较小的数,MSD适用于digits较大的数,关于radix sort原理可以查看下面的文章
Radix Sort
public class solution2 {
public int maximumGap(int[] nums) {
if (nums == null || nums.length < 2) return 0;
int len = nums.length;
int res = 0;
// use radix sort(bucket sort), LSD to sort the array
// create buckets using arraylist
// could also use a 2D array, but it takes more memory
ArrayList<Integer> buckets[] = new ArrayList[10];
// find the maximum number in the array
// in order to determine the number of runs
int maxNum = 0;
for (int i = 0; i < len; i++) {
maxNum = Math.max(maxNum, nums[i]);
}
int numLoop = 0;
int divide = 1;
while (maxNum/10 != 0) {
numLoop++;
divide*=10;
}
int lsd; // represents the index of bucket that a num should be put into
int temp = 1;
while (numLoop!=0) {
for (int i = 0; i < len; i++) {
lsd = (nums[i]/temp)%10;
// since the element will be appended to the end of the list
// use add()
buckets[lsd].add(nums[i]);
}
int counter = 0;// count the index of nums
for (int i = 0; i < 10; i++) {
while(!buckets[i].isEmpty()) {
nums[counter++] = buckets[i].get(0);
buckets[i].remove(0);
}
}
temp = temp*10;
numLoop--;
}
for (int i = 1; i < len; i++) {
res = Math.max(res, nums[i] - nums[i-1]);
}
return res;
}
}
复杂度分析:
时间:
O
(
d
∗
(
n
+
k
)
)
O(d*(n+k))
O(d∗(n+k)),d是counting sort的次数,和数组中最大数的位数有关,k是buckets的个数,此处为10.
空间:
O
(
n
)
O(n)
O(n)
在leetcode上提交时会提醒超出time limit,如[1, 100000]。主要是因为数组过小。
solution 3
同样采用radix sort但是对于bucket的size进行了优化.
首先可以确定的是对于一个大小为N的数组,其最小元素为min,最大元素为max,maximum gap至少大于等于
f
l
o
o
r
(
m
a
x
−
m
i
n
N
−
1
)
floor(\frac{max-min}{N - 1})
floor(N−1max−min)。可以采用反证法证明,假设均匀分布且,每个数之间的gap都恰好为
f
l
o
o
r
(
m
a
x
−
m
i
n
N
−
1
)
floor(\frac{max-min}{N - 1})
floor(N−1max−min),此时
m
a
x
=
m
i
n
+
g
a
p
∗
(
N
−
1
)
max = min + gap*(N-1)
max=min+gap∗(N−1) < max得到矛盾,说明最大gap至少大于等于
f
l
o
o
r
(
m
a
x
−
m
i
n
N
−
1
)
floor(\frac{max-min}{N - 1})
floor(N−1max−min)。
为了保证不需要比较每个bucket内部的数的大小,构造size为 f l o o r ( m a x − m i n N − 1 ) floor(\frac{max-min}{N - 1}) floor(N−1max−min)的bucket。在此情况下只需要比较不同bucket之间的最大gap。 同时bucket间的maximum gap只需要bucket内的最小最大值就可以得到。
注意点:
1 边际情况:全部是duplicates, [1,1,1,1],需要确保size>0
2 bucket个数:int bucketNum = (maxNum-minNum)/size + 1
3 preMax初始化
public class solution3 {
public int maximumGap(int[] nums) {
if (nums == null || nums.length < 2) return 0;
int len = nums.length;
int res = 0;
// find the maximum, minimum number in the array
// in order to determine the size of the bucket
int maxNum = Integer.MIN_VALUE;
int minNum = Integer.MAX_VALUE;
for (int i = 0; i < len; i++) {
maxNum = Math.max(maxNum, nums[i]);
minNum = Math.min(minNum, nums[i]);
}
// size is the maximum gap within a bucket
/*
note that if the array is filled with duplicates
like [1, 1, 1, 1], the maxNum - minNum == 0, and thus size == 0
which we need to avoid. Because when we calculate bucketNum,
(maxNum-minNum) is divided by size.
To sum up,we need to make sure size > 0
*/
int size = Math.max(1, (maxNum-minNum)/(len-1));
// define and initialize bucket
int bucketNum = (maxNum-minNum)/size + 1;
int[] bucketMin = new int[bucketNum];
int[] bucketMax = new int[bucketNum];
for (int i = 0; i < bucketNum; i++) {
bucketMax[i] = Integer.MIN_VALUE;
bucketMin[i] = Integer.MAX_VALUE;
}
// traverse the array and put numbers into buckets
for (int i = 0; i < len; i++) {
int index = (nums[i] - minNum)/size;
bucketMax[index] = Math.max(bucketMax[index], nums[i]);
bucketMin[index] = Math.min(bucketMin[index], nums[i]);
}
// as for the initialization of prevMax
// since the first bucket, bucket[0] can't be empty (minNum is in it)
// we can initialize prevMax with minNum
int prevMax = minNum;
for (int i = 0; i < bucketNum; i++) {
if (bucketMax[i] == Integer.MIN_VALUE && bucketMin[i] == Integer.MAX_VALUE)
// the bucket is empty
continue;
res = Math.max(res, bucketMin[i] - prevMax);
prevMax = bucketMax[i];
}
return res;
}
}