TAG
桶排序
浮點數不精確
方法
一眼看過去完全不知道該怎麼做。
看了題解覺得很高端的樣子,再後來多看了下,發現本質其實就是一個線性時間排序的問題!
線性時間排序,有計數排序、桶排序、基數排序。
計數排序時間複雜度與最大值與最小值差值有關。桶排序可以分桶,如果能夠把問題轉化到只計算桶間間距,那麼時間複雜度就能是與輸入個數成線性的了。基數排序沒有嘗試。
這道題官方題解是使用桶排序,每個桶只記錄落到這個桶的最大值與最小值。爲了使相鄰兩個數的最大間隔只存在兩個連續非空桶間(前一個數是前一個桶的最大值,後一個數是後一個桶的最小值),必須保證桶的個數大於等於輸入數據的個數。
爲何?利用類鴿巢原理,n個數,n個桶,要麼每個桶中一個數,要麼至少有一個桶爲空(暫且這麼瞎稱呼,正規的鴿巢原理是n+1個數,n個桶,則必然有一個桶至少有2個數)。如果每個桶中一個數,則連續非空桶間的最大間隔就是連續兩個數的最大間隔,成立。如果至少一個桶爲空,那麼必然有兩個連續的數,他們之間的間隔至少跨越了一個桶(表示的間隔),而在一個桶內的數,他們的間隔必然不會大於一個桶表示的間隔。
問題就變得簡單了。
然而還是遇到了坑——
設桶的個數爲輸入數據個數。當輸入數據只有2個時,桶的個數爲2;桶的間隔計算公式寫的是:
interval = static_cast<int>(
ceil(
( maxVal - minVal ) / float(nrBuckets - 1)
)
) ;
按理說肯定沒有錯的… minVal 必然落在第一個桶,maxVal落在第二個桶 ((maxVal - minVal) / interval == 1 ?
),然而卻錯了。
遇到了這個Case: [2,99999999]
,既然發現 static_cast<int>( ceil((9999999-2)/float(2-1)) )
值爲10^8
,不應該是99999997
嗎!!
浮點數就這麼不準嗎…
遇到這個坑後,當時解決辦法是用了精確ceil
.. 所以精確,就是先判斷能夠整除(模爲0),如果能則直接整數除法,如果不能,則整數除法再加1,這樣因爲是整數除法,所以就ok了。
int exactCeilDivide(int dividend, int divisor)
{
if(dividend % divisor == 0){ return dividend / divisor ;}
else { return dividend / divisor + 1;}
}
後來,我把桶設爲輸入數據數+1,即使使用ceil
函數也不會有問題了…
不過,或許不遇到這個坑,也不會注意到這個問題吧。
代碼
class Solution {
public:
int maximumGap(vector<int>& nums) {
size_t nrNum = nums.size() ;
if(nrNum < 2) return 0 ;
int minVal = *min_element(nums.cbegin(), nums.cend());
int maxVal = *max_element(nums.cbegin(), nums.cend());
if(minVal == maxVal) return 0; // edge condition
int nrBuckets = nrNum + 1;
int bucketRangeVal = static_cast<int>( ceil( (maxVal-minVal)/float(nrBuckets-1) ) );
vector<bucket> buckets(nrBuckets);
// dispatch
for(int num : nums)
{
int bucketIdx = ( num - minVal ) / bucketRangeVal ;
buckets[bucketIdx].fill(num);
}
// find max grap (only exists in different bucket)
int maxGap = 0;
auto aheadIter = buckets.begin() ; // min val must in the first bucket
while(true)
{
auto nextFilledIter = aheadIter + 1 ;
while(nextFilledIter < buckets.end() && !nextFilledIter->isFilled)
{
++nextFilledIter;
}
if(nextFilledIter >= buckets.end()) break;
maxGap = max(maxGap, get_bucket_gap(*aheadIter, *nextFilledIter));
aheadIter = nextFilledIter;
}
return maxGap;
}
private :
struct bucket
{
int minVal;
int maxVal;
bool isFilled;
bucket() : isFilled(false){}
void fill(int val)
{
if(!isFilled)
{
minVal = maxVal = val;
isFilled = true;
}
else
{
minVal = min(minVal, val);
maxVal = max(maxVal, val);
}
}
};
int get_bucket_gap(const bucket &ahead, const bucket &bebind)
{
return (bebind.minVal - ahead.maxVal);
}
};