Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

164. Maximum Gap - Leetcode Solution

Problem Description

The "Maximum Gap" problem asks you to find the largest difference between any two successive elements in the sorted form of a given unsorted array of non-negative integers, nums. Specifically, after sorting nums in ascending order, you must determine the maximum value of nums[i] - nums[i - 1] for all valid indices i (where 1 ≤ i < n, and n is the length of nums).

  • You must solve the problem in linear time and space, i.e., O(n) time and O(n) space.
  • If the array contains fewer than two elements, return 0.
  • Elements must not be reused to form the gap; only adjacent elements in the sorted array are considered.
  • There is always at least one valid solution for arrays of length 2 or more.

Thought Process

At first glance, the problem seems to require sorting the array and then finding the maximum difference between consecutive elements. This brute-force solution would take O(n \log n) time because of the sort operation. However, the problem explicitly demands a linear time solution.

To achieve O(n) time, we need to avoid general-purpose sorting. This leads us to consider bucket sort or radix sort, which can sort numbers in linear time under certain conditions. The key insight is that the maximum gap will not be within a bucket (since the bucket size is minimized), but rather between buckets. Thus, by distributing numbers into buckets and tracking the minimum and maximum of each bucket, we can compute the maximum gap efficiently.

Solution Approach

The optimal solution uses a bucket-based approach inspired by the pigeonhole principle. Here are the step-by-step details:

  1. Find the minimum and maximum values in the array: This establishes the overall range of values.
  2. Calculate the minimum possible maximum gap: If there are n numbers, the maximum gap is at least ceil((max - min) / (n - 1)). This value is used as the bucket size.
  3. Create buckets: We create n - 1 buckets, each able to store the minimum and maximum value that falls into that bucket.
  4. Distribute the numbers into buckets: For each number (except the global min and max), determine its bucket by computing (num - min) // bucket_size. Update the bucket's min and max accordingly.
  5. Scan through the buckets to find the maximum gap: The gap is always between the maximum of one bucket and the minimum of the next non-empty bucket.
  6. Return the largest gap found: This is the answer.

This approach guarantees an O(n) time and space solution because each step (finding min/max, distributing, and scanning) takes linear time.

Example Walkthrough

Let's walk through an example with nums = [3, 6, 9, 1]:

  • Step 1: min = 1, max = 9, n = 4
  • Step 2: bucket_size = ceil((9 - 1) / (4 - 1)) = ceil(8 / 3) = 3
  • Step 3: Create 3 buckets (n - 1 = 3)
  • Step 4: Place numbers into buckets (excluding min and max):
    • 3: (3 - 1) // 3 = 0 → Bucket 0
    • 6: (6 - 1) // 3 = 1 → Bucket 1
    • 9: skip (global max)
    • 1: skip (global min)
  • Step 5: Buckets after placement:
    • Bucket 0: min=3, max=3
    • Bucket 1: min=6, max=6
    • Bucket 2: empty
  • Step 6: Scan for max gap:
    • Initial previous = min = 1
    • Bucket 0: gap = 3 - 1 = 2
    • Bucket 1: gap = 6 - 3 = 3
    • Bucket 2: skip (empty)
    • Final gap: max - previous = 9 - 6 = 3
  • Step 7: The maximum gap is 3.

Time and Space Complexity

  • Brute-force approach:
    • Sort the array: O(n \log n)
    • Scan for max consecutive difference: O(n)
    • Total: O(n \log n) time, O(1) or O(n) space (depending on sort)
  • Optimized (bucket) approach:
    • Find min and max: O(n)
    • Distribute numbers: O(n)
    • Scan buckets: O(n)
    • Total: O(n) time, O(n) space for buckets

The optimized approach meets the problem's requirements for efficiency.

Summary

The "Maximum Gap" problem challenges us to find the largest difference between adjacent elements in a sorted version of an array, but under a strict linear time constraint. By leveraging the pigeonhole principle and using buckets to partition the value range, we efficiently compute the answer without explicit sorting. The solution is elegant because it transforms a seemingly sorting-dependent problem into a clever counting and partitioning exercise, making it both fast and memory-efficient.

Code Implementation

from math import ceil

class Solution:
    def maximumGap(self, nums):
        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 = ceil((max_num - min_num) / (n - 1))
        bucket_count = n - 1

        bucket_min = [float('inf')] * bucket_count
        bucket_max = [float('-inf')] * bucket_count

        for num in nums:
            if num == min_num or num == max_num:
                continue
            idx = (num - min_num) // bucket_size
            bucket_min[idx] = min(num, bucket_min[idx])
            bucket_max[idx] = max(num, bucket_max[idx])

        max_gap = 0
        prev = min_num
        for i in range(bucket_count):
            if bucket_min[i] == float('inf'):
                continue
            max_gap = max(max_gap, bucket_min[i] - prev)
            prev = bucket_max[i]
        max_gap = max(max_gap, max_num - prev)
        return max_gap
      
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;

class Solution {
public:
    int maximumGap(vector<int>& nums) {
        int n = nums.size();
        if (n < 2) return 0;
        int min_num = *min_element(nums.begin(), nums.end());
        int max_num = *max_element(nums.begin(), nums.end());
        if (min_num == max_num) return 0;
        int bucket_size = (max_num - min_num + n - 2) / (n - 1); // ceil
        int bucket_count = n - 1;
        vector<int> bucket_min(bucket_count, INT_MAX);
        vector<int> bucket_max(bucket_count, INT_MIN);

        for (int num : nums) {
            if (num == min_num || num == max_num) continue;
            int idx = (num - min_num) / bucket_size;
            bucket_min[idx] = min(bucket_min[idx], num);
            bucket_max[idx] = max(bucket_max[idx], num);
        }

        int max_gap = 0, prev = min_num;
        for (int i = 0; i < bucket_count; ++i) {
            if (bucket_min[i] == INT_MAX) continue;
            max_gap = max(max_gap, bucket_min[i] - prev);
            prev = bucket_max[i];
        }
        max_gap = max(max_gap, max_num - prev);
        return max_gap;
    }
};
      
class Solution {
    public int maximumGap(int[] nums) {
        int n = nums.length;
        if (n < 2) return 0;
        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
        for (int num : nums) {
            min = Math.min(min, num);
            max = Math.max(max, num);
        }
        if (min == max) return 0;
        int bucketSize = (int)Math.ceil((double)(max - min) / (n - 1));
        int bucketCount = n - 1;
        int[] bucketMin = new int[bucketCount];
        int[] bucketMax = new int[bucketCount];
        for (int i = 0; i < bucketCount; i++) {
            bucketMin[i] = Integer.MAX_VALUE;
            bucketMax[i] = Integer.MIN_VALUE;
        }
        for (int num : nums) {
            if (num == min || num == max) continue;
            int idx = (num - min) / bucketSize;
            bucketMin[idx] = Math.min(bucketMin[idx], num);
            bucketMax[idx] = Math.max(bucketMax[idx], num);
        }
        int maxGap = 0, prev = min;
        for (int i = 0; i < bucketCount; i++) {
            if (bucketMin[i] == Integer.MAX_VALUE) continue;
            maxGap = Math.max(maxGap, bucketMin[i] - prev);
            prev = bucketMax[i];
        }
        maxGap = Math.max(maxGap, max - prev);
        return maxGap;
    }
}
      
var maximumGap = function(nums) {
    if (nums.length < 2) return 0;
    let minNum = Math.min(...nums), maxNum = Math.max(...nums);
    if (minNum === maxNum) return 0;
    let n = nums.length;
    let bucketSize = Math.ceil((maxNum - minNum) / (n - 1));
    let bucketCount = n - 1;
    let bucketMin = new Array(bucketCount).fill(Infinity);
    let bucketMax = new Array(bucketCount).fill(-Infinity);

    for (let num of nums) {
        if (num === minNum || num === maxNum) continue;
        let idx = Math.floor((num - minNum) / bucketSize);
        bucketMin[idx] = Math.min(bucketMin[idx], num);
        bucketMax[idx] = Math.max(bucketMax[idx], num);
    }

    let maxGap = 0, prev = minNum;
    for (let i = 0; i < bucketCount; i++) {
        if (bucketMin[i] === Infinity) continue;
        maxGap = Math.max(maxGap, bucketMin[i] - prev);
        prev = bucketMax[i];
    }
    maxGap = Math.max(maxGap, maxNum - prev);
    return maxGap;
};