Published on

leetcode-164 Maximum Gap

Authors
  • avatar
    Name
    Gene Zhang
    Twitter

[164] Maximum Gap

Key Concept: Bucket Sort / Pigeonhole Principle - Maximum gap must be ≥ (max-min)/(n-1). Use buckets of this size. The maximum gap must occur between buckets, not within buckets.

Pattern: O(n) sorting using bucket/radix sort when comparison-based sorting is not allowed.

# Given an integer array nums, return the maximum difference between two
# successive elements in its sorted form. If the array contains less than
# two elements, return 0.
# You must write an algorithm that runs in linear time and uses linear extra space.
#
# Example 1:
# Input: nums = [3,6,9,1]
# Output: 3
# Explanation: Sorted form is [1,3,6,9], max gap = 3
#
# Example 2:
# Input: nums = [10]
# Output: 0
#
# Constraints:
# 1 <= nums.length <= 10^5
# 0 <= nums[i] <= 10^9

class Solution:
    # Approach 1: Bucket Sort (Optimal O(n))
    def maximumGap(self, nums: List[int]) -> int:
        if len(nums) < 2:
            return 0

        n = len(nums)
        min_val, max_val = min(nums), max(nums)

        if min_val == max_val:
            return 0

        # Bucket size (ceiling division)
        bucket_size = max(1, (max_val - min_val) // (n - 1))
        bucket_count = (max_val - min_val) // bucket_size + 1

        # Track min and max in each bucket
        buckets_min = [float('inf')] * bucket_count
        buckets_max = [float('-inf')] * bucket_count

        # Place each number in its bucket
        for num in nums:
            bucket_idx = (num - min_val) // bucket_size
            buckets_min[bucket_idx] = min(buckets_min[bucket_idx], num)
            buckets_max[bucket_idx] = max(buckets_max[bucket_idx], num)

        # Find maximum gap between buckets
        max_gap = 0
        prev_max = min_val

        for i in range(bucket_count):
            # Skip empty buckets
            if buckets_min[i] == float('inf'):
                continue

            # Gap is between previous bucket's max and current bucket's min
            max_gap = max(max_gap, buckets_min[i] - prev_max)
            prev_max = buckets_max[i]

        return max_gap

    # Approach 2: Radix Sort (also O(n))
    def maximumGap2(self, nums: List[int]) -> int:
        if len(nums) < 2:
            return 0

        # Radix sort
        def radix_sort(arr):
            max_val = max(arr)
            exp = 1

            while max_val // exp > 0:
                counting_sort(arr, exp)
                exp *= 10

        def counting_sort(arr, exp):
            n = len(arr)
            output = [0] * n
            count = [0] * 10

            # Count occurrences
            for num in arr:
                index = (num // exp) % 10
                count[index] += 1

            # Cumulative count
            for i in range(1, 10):
                count[i] += count[i - 1]

            # Build output array
            for i in range(n - 1, -1, -1):
                index = (arr[i] // exp) % 10
                output[count[index] - 1] = arr[i]
                count[index] -= 1

            # Copy to original array
            for i in range(n):
                arr[i] = output[i]

        radix_sort(nums)

        # Find max gap in sorted array
        max_gap = 0
        for i in range(1, len(nums)):
            max_gap = max(max_gap, nums[i] - nums[i - 1])

        return max_gap

# Bucket Sort: Time O(n), Space O(n)
# Radix Sort: Time O(d*n) where d is digits, Space O(n)
#
# Key Insight (Bucket Sort):
# - Pigeonhole principle: With n numbers in range [min, max],
#   maximum gap ≥ ceiling((max-min)/(n-1))
# - Use buckets of this size
# - Maximum gap must be between buckets, not within buckets
# - Only need to track min and max of each bucket
#
# Why maximum gap is between buckets?
# - Average gap = (max-min)/(n-1)
# - If max gap were within a bucket, all gaps would be ≤ bucket_size
# - But we chose bucket_size = ceiling(average gap)
# - So max gap must be between buckets!
#
# AirBnB: Tests understanding of non-comparison based sorting