Binary Search To Find An Answer

public int findDuplicate(int[] nums) {
    int lo = 1, hi = nums.length - 1;
    while (lo < hi) {
        int mid = lo + (hi - lo) / 2;
        if (findSmall(nums, mid)) {
            lo = mid + 1;
        } else {
            hi = mid;
        }
    }
    return lo;
}

private boolean findSmall(int[] nums, int target) {
    int count = 0;
    for (int num : nums) {
        if (num <= target) {
            count++;
        }
    }
    return count <= target;
}

For problems such as 410, 875, 1011, 1231, it is hard to start. It has pre-conditions:

  • Choose data from an array

  • Must choose consecutively

  • Can be determined by "try out"

  • Asking for a "min" or "max" of the choice

The problem asks if can be divide the array by a number and having the min/max of this range. Search in a range between the max of all numbers and the sum of all numbers. Use a helper function to try all numbers of the range, and find the min/max.

Instead of trying all numbers, we can use binary search to quickly determine the result.

T: O(N*log(range_size)) S: O(1)

public int shipWithinDays(int[] weights, int D) {
    int max = Integer.MIN_VALUE;
    int sum = 0;
    for (int w : weights) {
        max = Math.max(max, w);
        sum += w;
    }
    int left = max, right = sum;
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (canDivide(weights, mid, D)) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

/*
*Determine if the array can split into D continuous subarrays, 
 and each subarray sum can fit (<=) capacity
*/
private boolean canDivide(int[] weights, int capacity, int D) {
    int sum = 0, count = 1;
    for (int w : weights) {
        if (sum + w > capacity) {
            sum = 0;
            if (count++ > D) return false;
        }
        sum += w;
    }
    return count <= D;
}

public int splitArray(int[] nums, int m) {
    int sum = 0, max = Integer.MIN_VALUE;
    for (int n : nums) {
        sum += n;
        max = Math.max(max, n);
    }
    
    int lo = max, hi = sum;
    while (lo < hi) {
        int mid = lo + (hi - lo) / 2;
        if (canDivide(nums, mid, m)) {
            hi = mid;
        } else {
            lo = mid + 1;
        }
    }
    return lo;
}

/*
*Determine if the array can split into m continuous subarrays, 
 and each subarray sum is smaller than sum val
*/
private boolean canDivide(int[] nums, int val, int m) {
    int sum = 0, count = 1;
    for (int n : nums) {
        if (sum + n > val) {
            count++;
            sum = 0;
            if (count > m) {
                return false;
            }
        }
        sum += n;
    }
    return true;
}

Divide the array into K continuous subarrays, maximize the min share of this array

public int maximizeSweetness(int[] sweetness, int K) {
    int sum = 0, min = sweetness[0];
    for (int n : sweetness) {
        sum += n;
        min = Math.min(min, n);
    }
    int L = min, R = sum;
    while (L < R) {
        int mid = L + (R - L + 1) / 2;
        if (canDivide(sweetness, mid, K)) {
            L = mid;
        } else {
            R = mid - 1;
        }
    }
    return L;
}

private boolean canDivide(int[] sweet, int t, int K) {
    int groupcount = 0, sum = 0;
    for (int n : sweet) {
        sum += n;
        if (sum >= t) {
            groupcount++;
            sum = 0;
        }
    }
    return groupcount > K;
}

public int minEatingSpeed(int[] piles, int H) {
    int right = 0;
    for (int pile : piles) {
        right = Math.max(right, pile);
    }
    int left = 0;
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (canEatAll(piles, H, mid)) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

private boolean canEatAll(int[] piles, int h, int k) {
    int hours = 0;
    for (int p : piles) {
        hours += Math.ceil(1.0 * p / k);
    }
    return hours <= h;
}

public double minmaxGasDist(int[] stations, int k) {
    double left = 0, right = 1e8;
    while (right - left > 1e-6) {
        double mid = (left + right) / 2.0;
        if (isValid(stations, k, mid)) {
            right = mid;
        } else {
            left = mid;
        }
    }
    return left;
}

private boolean isValid(int[] stations, int k, double d) {
    int used = 0;
    for (int i = 0; i < stations.length - 1; i++) {
        used += (int) ((stations[i + 1] - stations[i]) / d);
    }
    return used <= k;
}

Find the Smallest Divisor Given a Threshold

Divide Chocolate

424. Longest Repeating Character Replacement

1802. Maximum Value at a Given Index in a Bounded Array

public int maxValue(int n, int index, int maxSum) {
    int left = 1, right = maxSum;
    while (left < right) {
        int mid = (left + right + 1) / 2;
        if (getSum(index, mid, n) <= maxSum) {
            left = mid;
        } else {
            right = mid - 1;
        }
    }
    return left;
}

private long getSum(int index, int value, int n) {
    long count = 0;
    
    // On index's left:
    // If value > index, there are index + 1 numbers in the arithmetic sequence:
    // [value - index, ..., value - 1, value].
    // Otherwise, there are value numbers in the arithmetic sequence:
    // [1, 2, ..., value - 1, value], plus a sequence of length (index - value + 1) of 1s. 
    if (value > index) {
        count += (long)(value + value - index) * (index + 1) / 2;
    } else {
        count += (long)(value + 1) * value / 2 + index - value + 1;
    };
    // On index's right:
    // If value >= n - index, there are n - index numbers in the arithmetic sequence:
    // [value, value - 1, ..., value - n + 1 + index].
    // Otherwise, there are value numbers in the arithmetic sequence:
    // [value, value - 1, ..., 1], plus a sequence of length (n - index - value) of 1s. 
    if (value >= n - index) {
        count += (long)(value + value - n + 1 + index) * (n - index) / 2;
    } else {
        count += (long)(value + 1) * value / 2 + n - index - value;
    }   
    
    return count - value;
}

2616. Minimize the Maximum Difference of Pairs

public int minimizeMax(int[] nums, int p) {
    Arrays.sort(nums);
    int n = nums.length;
    int left = 0, right = nums[n - 1] - nums[0];
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (countValidPairs(nums, mid) >= p) {
            right = mid;
        } else {
            left = mid + 1;
        }
    } 
    return left;
}

private int countValidPairs(int[] nums, int threshold) {
    int i = 0, count = 0;
    while (i < nums.length - 1) {
        if (nums[i + 1] - nums[i] <= threshold) {
            count++;
            i += 2; // skip the next one because it forms a pair with current
        } else {
            i++;
        }
    }
    return count;
}

(NOT on LC) Maximum min absolute difference

Problem: Given an integer array nums and an integer d, you can adjust each element of nums by adding any value up to d (including zero). Your goal is to modify the array such that the minimum absolute difference between any two elements in the adjusted array is maximized. Example: Given nums = [6, 0, 3] and d = 2, one possible adjusted array is [8, 0, 4]. The minimum absolute difference among these elements is 4 (the minimum of |8−4|, |4−0|, and |8−0|). Therefore, the maximum achievable minimum difference is 4. Given nums = [1, 5, 9, 14] and d = 3, you can adjust each element by adding any value from 0 up to 3. One possible adjusted array is [4, 5, 9, 14]. The pairwise absolute differences are: |4 - 5| = 1 |5 - 9| = 4 |9 - 14| = 5 and others like |4 - 9| = 5, |5 - 14| = 9, |4 - 14| = 10 The minimum difference here is 1. But adjusting to [1, 5, 12, 14] gives differences: |1 - 5| = 4 |5 - 12| = 7 |12 - 14| = 2 others: |1 - 12| = 11, |1 - 14| = 13, |5 - 14| = 9

The minimum difference is now 2, which is larger than before. Your goal is to find such adjustments that maximize this minimum absolute difference.

public int maximizeMinDifference(int[] nums, int d) {
    Arrays.sort(nums);
    int n = nums.length;
    int low = 0;
    int high = nums[n - 1] + d - nums[0];  // max possible difference

    while (low <= high) {
        int mid = low + (high - low) / 2;
        if (canAdjust(nums, d, mid)) {
            low = mid + 1;  // try larger minimum difference
        } else {
            high = mid - 1; // try smaller
        }
    }
    return high;
}

private boolean canAdjust(int[] nums, int d, int minDiff) {
    // Adjust first element minimally
    long prev = nums[0];
    for (int i = 1; i < nums.length; i++) {
        // The minimum value to maintain minDiff from prev
        long target = prev + minDiff;
        // The adjusted value must be in [nums[i], nums[i] + d]
        if (target <= nums[i] + d) {
            // Assign the max between target and nums[i]
            prev = Math.max(target, nums[i]);
        } else {
            // Cannot maintain minDiff
            return false;
        }
    }
    return true;
}

Last updated

Was this helpful?