题目描述
给定 n 个实数,求这n个实数在数轴上相邻2个数之间的最大差值,设计解最大间隙问题的线性时间算法(时间复杂度为O(n))。
输入
数组
输出
最大间隙
样例输入
1,2,4,3,5,8.5,7,8,10,19
样例输出
9
解题思路:
首先了解一下鸽笼原理(抽屉原理):把n个物品放到n-1个抽屉里,不管怎么分,则至少有一个桶空着!这个思想有用到。
回到这道题,如何理解解题算法:
我们假定有N = 10的数组array【1,2,4,3,5,8.5,7,8,10,19】这10个数字,欲找到里面最大相邻间隙。
1.分区间:
该算法的意思是先找到最大值 MAX 和最小值 MIN,然后用 ( MAX - MIN ) ÷ ( N-1 ) ;求得一个区间长度 len=2,这波操作相当于我们以 len 为梯度分了10个区间;也就是常说的分了10个桶。
2.放数字:
下一步我们需要将【1,2,4,3,5,8.5,7,8,10,19】这10个数字放到这10个区间里。用一个公式来放:
(int)( (array[i]-MIN) / len)
这个公式会把每个数按大小公平地划分到对应的梯队里面 !
例如:
-
数字 1 : (1 - 1)/ 2 = 0 ,所以数字1放在了0号区间里,
-
数字 2 : (2 - 1)/ 2 = 0 ,所以数字2放在了0号区间里,
-
数字 4 : (4 - 1)/ 2 = 1 ,所以数字3放在了1号区间里,
… -
数字 10 : (10 - 1)/ 2 = 4,所以数字10放在了4号区间里,
-
数字 19 : (19 - 1)/ 2 = 9 ,这里所以数字19放在了9号区间里;
如下:
下一步我们把每个区间只留下最大值和最小值。
用一个规则,没有数字的区间不管,只有一个数字那么最大值和最小值都是它,有两个数以上就只留下最大值最小值。换成编程语言意思就是只记录最大值最小值。
如下:
4.求最大间隙:
现在就可以求最大间隙了,它存在于某一个区间地最大值与下一个区间最小值差值里面!也就是说某区间最大值和下一个区间最小值的差值集的最大值就是原来数组的最大间隙~
怎么理解:因为每个区间里的数字都你是按大小分的,那么同一个区间内的最大值最小值差值必然不会超过区间len!如果超过了len,那么这个区间里面的最大数早就按公式被分到下一个区间里去了,或者最小值早就被分到上一个梯度的区间了,所以能跳出len的长度的间隙一定只存在于两个区间之间。
LeetCode 代码:
int maximumGap(vector<int> &nums) {
int N = nums.size();
if(N < 2){
return 0;
}
int count[N] = {0},i;
double max[N];
double min[N];
/*找出梯队长度*/
// (MAX - MIN ) / (N-1)
double MAX = nums[0], MIN = nums[0];
for(i = 1 ; i < N ; i++) {
if ( nums[i] > MAX ) {
MAX = nums[i];
}
if( nums[i] < MIN) {
MIN = nums[i];
}
}
if(MAX - MIN == 0){
return 0;
}
double len = (MAX - MIN ) / (N-1) ;
/*对桶进行初始化*/
//每个桶的最大值初始化了一个最小数
//每个桶的最大值初始化了一个最小数
//这样是为了保证至少能够被array中的最大或最小数替换掉
//虽然定义了N个桶,但必然至少有一个桶是不会被用的
for(i=0;i<N;i++){
max[i]=MIN;
min[i]=MAX;
count[i]=0;
}
/*把n个数放到n-1个桶中,必然至少一个桶没放数,至少一个,因为我定义了n个桶*,而又是按n-1划分的*/
//每个数如何找到正确的桶下标index?公式:int index = (int)( (nums[i]-min) / len)
int index;
for(i=0;i<N;i++){
index=(int)( (nums[i]-MIN) / len);
if(max[index] < nums[i])
max[index]=nums[i];
if(min[index] > nums[i])
min[index]=nums[i];
count[index]++;
}
/*计算最大间隙*/
//去掉空桶 生成一个新数组
int x,y;
double Gap = len,last,now;
for(i=0;i<N-1;){
x = i;
last = max[x];
if( count[i+1] == 0 ){
while(count[++i] == 0);
y=i;
}else{
y = i+1;
}
now = min[y];
if(now - last > Gap){
Gap = now - last;
}
i = y;
}
return Gap;
}
写全一点:
#include <stdio.h>
#define N 5
//待排序数组
double array[N] = {2.1,3.1,1.5,0.3,7.5};
int main(){
//只有一个数没有间隙
if(N < 2){
return 0
}
/*对桶进行初始化*/
//每个桶的最大值初始化了一个最小数
//每个桶的最大值初始化了一个最小数
//这样是为了保证至少能够被array中的最大或最小数替换掉
//虽然定义了N个桶,但必然至少有一个桶是不会被用的
//记录每个桶每个桶被分到的数字
int count[N] = {0};
//记录每个桶中最大值
double max[N];
//记录每个分桶中最小值
double min[N];
int i;
for(i=0;i<N;i++){
max[i]=smallest;
min[i]=biggest;
count[i]=0;
}
/*找出梯队长度*/
// (MAX - MIN ) / (N-1)
double MAX = array[0], MIN = array[0];
for(i = 1 ; i < N ; i++) {
if (array[i] > MAX ) {
MAX = array[i];
}
if(array[i] < MIN) {
MIN = array[i];
}
}
// 数字全都一样的情况
if(MAX - MIN == 0){
return 0;
}
double len = (MAX - MIN ) / (N-1) ;
/*把n个数放到n-1个桶中,必然至少一个桶没放数,至少一个,因为我定义了n个桶*,而又是按n-1划分的*/
int index;
for(i=0;i<N;i++){
index=(int)( (array[i]-MIN) / len);
if(max[index] < array[i])
max[index]=array[i];
if(min[index] > array[i])
min[index]=array[i];
count[index]++;
}
/*计算最大间隙*/
int x,y;
double Gap = len,last,now;
for(i=0;i<N-1;){
x = i;
last = max[x];
if( count[i+1] == 0 ){
while(count[++i] == 0);
y=i;
}else{
y = i+1;
}
now = min[y];
if(now - last > Gap){
Gap = now - last;
}
i = y;
}
printf("%lf",Gap);
return 0;
}