Minimum Difference in Sums After Removal of Elements

hard heap prefix / suffix greedy

Problem

You are given an array nums of 3n elements. Remove any subsequence of exactly n elements. The remaining 2n elements keep their order: the first n form part one with sum sumfirst, the next n form part two with sum sumsecond. Return the minimum possible value of sumfirst − sumsecond.

To minimize the difference we want sumfirst as small as possible and sumsecond as large as possible. Choose a split index i: take the smallest n values from the prefix nums[0..i) and the largest n values from the suffix nums[i..3n). A size-n max-heap streams the prefix minimum, a size-n min-heap streams the suffix maximum.

Inputnums = [7,9,5,8,1,3]
Output1
n = 2. Remove 9 and 1, leaving [7,5,8,3]: (7+5) − (8+3) = 12 − 11 = 1, the smallest achievable.

import heapq

def minimumDifference(nums):
    m = len(nums); n = m // 3
    # pref[i] = min sum of n elements among nums[0:i]
    pref = [0] * (m + 1)
    max_heap = []        # negatives: keeps the smallest n seen
    s = 0
    for i in range(2 * n):
        heapq.heappush(max_heap, -nums[i]); s += nums[i]
        if len(max_heap) > n:
            s += heapq.heappop(max_heap)   # drop the largest kept
        if len(max_heap) == n:
            pref[i + 1] = s
    # suf[i] = max sum of n elements among nums[i:m]
    suf = [0] * (m + 1)
    min_heap = []        # keeps the largest n seen
    s = 0
    for i in range(m - 1, n - 1, -1):
        heapq.heappush(min_heap, nums[i]); s += nums[i]
        if len(min_heap) > n:
            s -= heapq.heappop(min_heap)   # drop the smallest kept
        if len(min_heap) == n:
            suf[i] = s
    ans = float('inf')
    for i in range(n, 2 * n + 1):          # split point
        ans = min(ans, pref[i] - suf[i])
    return ans
function minimumDifference(nums) {
  const m = nums.length, n = (m / 3) | 0;
  const pref = new Array(m + 1).fill(0);
  const maxHeap = new MaxHeap();        // keeps the smallest n
  let s = 0;
  for (let i = 0; i < 2 * n; i++) {
    maxHeap.push(nums[i]); s += nums[i];
    if (maxHeap.size() > n) s -= maxHeap.pop();   // drop largest kept
    if (maxHeap.size() === n) pref[i + 1] = s;
  }
  const suf = new Array(m + 1).fill(0);
  const minHeap = new MinHeap();        // keeps the largest n
  s = 0;
  for (let i = m - 1; i >= n; i--) {
    minHeap.push(nums[i]); s += nums[i];
    if (minHeap.size() > n) s -= minHeap.pop();   // drop smallest kept
    if (minHeap.size() === n) suf[i] = s;
  }
  let ans = Infinity;
  for (let i = n; i <= 2 * n; i++)       // split point
    ans = Math.min(ans, pref[i] - suf[i]);
  return ans;
}
long minimumDifference(int[] nums) {
    int m = nums.length, n = m / 3;
    long[] pref = new long[m + 1];
    PriorityQueue<Integer> maxHeap =     // keeps the smallest n
        new PriorityQueue<>(Collections.reverseOrder());
    long s = 0;
    for (int i = 0; i < 2 * n; i++) {
        maxHeap.add(nums[i]); s += nums[i];
        if (maxHeap.size() > n) s -= maxHeap.poll();  // drop largest
        if (maxHeap.size() == n) pref[i + 1] = s;
    }
    long[] suf = new long[m + 1];
    PriorityQueue<Integer> minHeap = new PriorityQueue<>();
    s = 0;
    for (int i = m - 1; i >= n; i--) {
        minHeap.add(nums[i]); s += nums[i];
        if (minHeap.size() > n) s -= minHeap.poll();  // drop smallest
        if (minHeap.size() == n) suf[i] = s;
    }
    long ans = Long.MAX_VALUE;
    for (int i = n; i <= 2 * n; i++)      // split point
        ans = Math.min(ans, pref[i] - suf[i]);
    return ans;
}
long long minimumDifference(vector<int>& nums) {
    int m = nums.size(), n = m / 3;
    vector<long long> pref(m + 1, 0), suf(m + 1, 0);
    priority_queue<int> maxHeap;          // keeps the smallest n
    long long s = 0;
    for (int i = 0; i < 2 * n; i++) {
        maxHeap.push(nums[i]); s += nums[i];
        if ((int)maxHeap.size() > n) { s -= maxHeap.top(); maxHeap.pop(); }
        if ((int)maxHeap.size() == n) pref[i + 1] = s;
    }
    priority_queue<int, vector<int>, greater<int>> minHeap;
    s = 0;
    for (int i = m - 1; i >= n; i--) {
        minHeap.push(nums[i]); s += nums[i];
        if ((int)minHeap.size() > n) { s -= minHeap.top(); minHeap.pop(); }
        if ((int)minHeap.size() == n) suf[i] = s;
    }
    long long ans = LLONG_MAX;
    for (int i = n; i <= 2 * n; i++)      // split point
        ans = min(ans, pref[i] - suf[i]);
    return ans;
}
Time: O(n log n) Space: O(n)