🧠
Bucket Sort / Pigeonhole Principle
💡 This approach exists to achieve linear time by avoiding full sorting. It introduces the pigeonhole principle to distribute numbers into buckets and find max gap between buckets.
Intuition
Divide the range between min and max into buckets so that the maximum gap must be between buckets, not inside them.
Algorithm
- Find the minimum and maximum values in the array.
- Calculate bucket size and number of buckets based on the range and array length.
- Place each number into its corresponding bucket, tracking bucket min and max.
- Scan buckets to find the maximum gap between the max of one bucket and the min of the next non-empty bucket.
💡 The key insight is that the maximum gap cannot be inside a bucket but only between buckets.
from typing import List
def maximumGap(nums: List[int]) -> int:
if len(nums) < 2:
return 0
min_num, max_num = min(nums), max(nums)
if min_num == max_num:
return 0
n = len(nums)
bucket_size = max(1, (max_num - min_num) // (n - 1))
bucket_count = (max_num - min_num) // bucket_size + 1
buckets_min = [float('inf')] * bucket_count
buckets_max = [float('-inf')] * bucket_count
for num in nums:
idx = (num - min_num) // bucket_size
buckets_min[idx] = min(buckets_min[idx], num)
buckets_max[idx] = max(buckets_max[idx], num)
max_gap = 0
prev_max = buckets_max[0]
for i in range(1, bucket_count):
if buckets_min[i] == float('inf'):
continue
max_gap = max(max_gap, buckets_min[i] - prev_max)
prev_max = buckets_max[i]
return max_gap
# Driver code
if __name__ == '__main__':
print(maximumGap([3,6,9,1])) # Output: 3
Line Notes
min_num, max_num = min(nums), max(nums)Find range boundaries
bucket_size = max(1, (max_num - min_num) // (n - 1))Calculate minimum bucket size to ensure max gap between buckets
buckets_min = [float('inf')] * bucket_countInitialize bucket mins to infinity
for num in nums:Place each number in its bucket and update min/max
import java.util.*;
public class MaximumGap {
public static int maximumGap(int[] nums) {
if (nums.length < 2) return 0;
int minNum = Integer.MAX_VALUE, maxNum = Integer.MIN_VALUE;
for (int num : nums) {
minNum = Math.min(minNum, num);
maxNum = Math.max(maxNum, num);
}
if (minNum == maxNum) return 0;
int n = nums.length;
int bucketSize = Math.max(1, (maxNum - minNum) / (n - 1));
int bucketCount = (maxNum - minNum) / bucketSize + 1;
int[] bucketsMin = new int[bucketCount];
int[] bucketsMax = new int[bucketCount];
Arrays.fill(bucketsMin, Integer.MAX_VALUE);
Arrays.fill(bucketsMax, Integer.MIN_VALUE);
for (int num : nums) {
int idx = (num - minNum) / bucketSize;
bucketsMin[idx] = Math.min(bucketsMin[idx], num);
bucketsMax[idx] = Math.max(bucketsMax[idx], num);
}
int maxGap = 0, prevMax = bucketsMax[0];
for (int i = 1; i < bucketCount; i++) {
if (bucketsMin[i] == Integer.MAX_VALUE) continue;
maxGap = Math.max(maxGap, bucketsMin[i] - prevMax);
prevMax = bucketsMax[i];
}
return maxGap;
}
public static void main(String[] args) {
System.out.println(maximumGap(new int[]{3,6,9,1})); // Output: 3
}
}
Line Notes
int minNum = Integer.MAX_VALUE, maxNum = Integer.MIN_VALUE;Initialize min and max to extremes
int bucketSize = Math.max(1, (maxNum - minNum) / (n - 1));Calculate bucket size to guarantee max gap between buckets
Arrays.fill(bucketsMin, Integer.MAX_VALUE);Initialize bucket mins
for (int num : nums) {Assign numbers to buckets and update min/max
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int maximumGap(vector<int>& nums) {
if (nums.size() < 2) return 0;
int minNum = *min_element(nums.begin(), nums.end());
int maxNum = *max_element(nums.begin(), nums.end());
if (minNum == maxNum) return 0;
int n = nums.size();
int bucketSize = max(1, (maxNum - minNum) / (n - 1));
int bucketCount = (maxNum - minNum) / bucketSize + 1;
vector<int> bucketsMin(bucketCount, INT_MAX);
vector<int> bucketsMax(bucketCount, INT_MIN);
for (int num : nums) {
int idx = (num - minNum) / bucketSize;
bucketsMin[idx] = min(bucketsMin[idx], num);
bucketsMax[idx] = max(bucketsMax[idx], num);
}
int maxGap = 0, prevMax = bucketsMax[0];
for (int i = 1; i < bucketCount; i++) {
if (bucketsMin[i] == INT_MAX) continue;
maxGap = max(maxGap, bucketsMin[i] - prevMax);
prevMax = bucketsMax[i];
}
return maxGap;
}
int main() {
vector<int> nums = {3,6,9,1};
cout << maximumGap(nums) << endl; // Output: 3
return 0;
}
Line Notes
int minNum = *min_element(nums.begin(), nums.end());Find minimum element
int bucketSize = max(1, (maxNum - minNum) / (n - 1));Calculate bucket size to ensure max gap between buckets
vector<int> bucketsMin(bucketCount, INT_MAX);Initialize bucket mins to max int
for (int num : nums) {Assign each number to a bucket and update min/max
function maximumGap(nums) {
if (nums.length < 2) return 0;
let minNum = Math.min(...nums);
let maxNum = Math.max(...nums);
if (minNum === maxNum) return 0;
const n = nums.length;
const bucketSize = Math.max(1, Math.floor((maxNum - minNum) / (n - 1)));
const bucketCount = Math.floor((maxNum - minNum) / bucketSize) + 1;
const bucketsMin = new Array(bucketCount).fill(Infinity);
const bucketsMax = new Array(bucketCount).fill(-Infinity);
for (const num of nums) {
const idx = Math.floor((num - minNum) / bucketSize);
bucketsMin[idx] = Math.min(bucketsMin[idx], num);
bucketsMax[idx] = Math.max(bucketsMax[idx], num);
}
let maxGap = 0, prevMax = bucketsMax[0];
for (let i = 1; i < bucketCount; i++) {
if (bucketsMin[i] === Infinity) continue;
maxGap = Math.max(maxGap, bucketsMin[i] - prevMax);
prevMax = bucketsMax[i];
}
return maxGap;
}
// Test
console.log(maximumGap([3,6,9,1])); // Output: 3
Line Notes
let minNum = Math.min(...nums);Find minimum number in array
const bucketSize = Math.max(1, Math.floor((maxNum - minNum) / (n - 1)));Calculate bucket size to ensure max gap between buckets
const bucketsMin = new Array(bucketCount).fill(Infinity);Initialize bucket mins to Infinity
for (const num of nums) {Place each number into its bucket and update min/max
Finding min/max and bucket assignment are linear; scanning buckets is linear too.
💡 For n=100,000, this approach avoids sorting and runs in about 100,000 operations, much faster than sorting.
Interview Verdict: Accepted
This is the optimal approach for large inputs and is preferred in interviews when linear time is required.