題目描述
給定 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;
}