問題描述:已知一個整數數組a[],其長度為n,要找出數組中相鄰元素的最大間距。
分析:問題很簡單,而且描述本身就暗示了一種自然的求解方法,即先對元素排序,然后逐個求相鄰元素的間距。這種解法的復雜度為O(n*logn)。再想一下,設min, max分別是數組中的最小和最大元素,len = (max-min)/n-1那么最大間距maxGap >= len。把a分成長度為len的區間,即[min, min+len), [min+len, min+2*len), ..., [max-len, max],最大間距一定是落在相鄰的兩個區間,或者不相鄰但中間區間沒有元素的兩個區間,並且是在前一個區間的最大值和后一個區間的最小值之間產生。所以只需找出每一個區間的最大值和最小值,然后掃描一遍就能求出最大間距。
#define MAXINT (1<<31)-1
#define MININT 1<<31
typedef struct _bucket { int min, max; } Bucket; int maxGap(int *a, int n) { int max = MININT, min = MAXINT, i; double bucketLen; for(i = 0; i < n; ++i) { if(a[i] > max) max = a[i]; if(a[i] < min) min = a[i]; } bucketLen = (max-min) * 1.0 / (n-1); Bucket *bucket = new Bucket[n-1]; for(i = 0; i < n-1; ++i) { bucket[i].max = MININT; bucket[i].min = MAXINT; } bucket[0].max = min; bucket[0].min = min; bucket[n-2].max = max; bucket[n-2].min = max; for(i = 0; i < n; ++i) { int index; if(a[i] != min && a[i] != max) { index = (a[i]-min) * 1.0 / bucketLen; if(bucket[index].max < a[i]) bucket[index].max = a[i]; if(bucket[index].min > a[i]) bucket[index].min = a[i]; } } int low = -1, high = -1, maxGap = 0, curGap, pre = 0; for(i = 1; i < n-1; ++i) { if(bucket[i].max != MININT) { curGap = bucket[i].min - bucket[pre].max; if(curGap > maxGap) { maxGap = curGap; low = bucket[pre].max, high = bucket[i].min; } pre = i; } } printf("the maximum gap is %d, between %d and %d!\n", maxGap, low, high); return maxGap; } int main() { int a[] = {1,8,4,5,6}; maxGap(a,5); return 0; }
這種將元素分段(也可以叫做分桶)的思想也可用於求解找出位於多個不同機器上的第k大元素的問題,這類問題的描述如下:
有n個不同位置的主機,每個主機上有很多的整數,要求找出所有主機上的第k大到第k+m大的元素。算法應該使主機之間的通信量盡可能地小。
算法思想:
1. 選一個適當的桶大小len, 統計每個主機落在每個桶中的元素數目
2. 將1得到的統計信息匯總起來,確定第k大到第k+m大的元素位於哪些桶中
3. 將各個主機上這些桶中的元素集中到一起,再用排序或者最小堆找出第k大元素到第k+m大元素
這種方法關鍵在於桶大小的選取,太大的話上面算法第3步傳輸的數據會比較多,太小第2步傳輸的統計信息又會比較多。理想狀態下第k大到第k+m大的元素位於一個桶中,這樣只需要傳輸一個桶大小的數據。所以,可以把桶大小選為和m一個量級。