LeetCode Daily Challenge-2163: Minimum Difference in Sums After Removal of Elements(Hard, Java, DP+priorityQueue+greedy)

Anni HuangAnni Huang
6 min read

Problem Statement

You are given a 0-indexed integer array nums consisting of 3 * n elements. You are allowed to remove any subsequence of elements of size exactly n from nums. The remaining 2 * n elements will be divided into two equal parts:

  • The first n elements belonging to the first part and their sum is sumfirst
  • The next n elements belonging to the second part and their sum is sumsecond

The difference in sums of the two parts is denoted as sumfirst - sumsecond.

Return the minimum difference possible between the sums of the two parts after the removal of n elements.

Examples

Example 1:

Input: nums = [3,1,2]
Output: -1
Explanation: n = 1, so we remove 1 element and split into two parts of 1 element each.
- Remove 3: [1,2] → 1 - 2 = -1
- Remove 1: [3,2] → 3 - 2 = 1  
- Remove 2: [3,1] → 3 - 1 = 2
Minimum difference = -1

Example 2:

Input: nums = [7,9,5,8,1,3]
Output: 1
Explanation: n = 2, so we remove 2 elements and split into two parts of 2 elements each.
Optimal: Remove 9 and 1 → [7,5,8,3] → (7+5) - (8+3) = 1

Constraints

  • nums.length == 3 * n
  • 1 <= n <= 10^5
  • 1 <= nums[i] <= 10^5

Algorithm Walkthrough

Key Insight

To minimize sumfirst - sumsecond, we want:

  • sumfirst to be as small as possible
  • sumsecond to be as large as possible

The crucial observation is that after removing n elements, the remaining 2n elements maintain their relative positions. We can think of this as choosing a "split point" where:

  • The first part takes the n smallest available elements from the left portion
  • The second part takes the n largest available elements from the right portion

Strategy

  1. Precompute left minimums: For each position i, calculate the minimum possible sum of n elements from nums[0...i]

  2. Precompute right maximums: For each position i, calculate the maximum possible sum of n elements from nums[i...3n-1]

  3. Find optimal split: Try all valid split points and find the minimum difference

Step-by-Step Implementation

Step 1: Build Left Minimums Array

We use a max heap to maintain the n smallest elements seen so far:

// leftMins[i] = minimum sum of n elements from nums[0...i]
for (int i = 0; i < 2 * n; i++) {
    maxHeap.offer(nums[i]);
    sum += nums[i];

    if (maxHeap.size() > n) {
        sum -= maxHeap.poll();  // Remove largest element
    }

    if (maxHeap.size() == n) {
        leftMins[i] = sum;
    }
}

Why max heap? We want to keep the n smallest elements. When we encounter a new element smaller than our current largest small element, we replace the largest.

Step 2: Build Right Maximums Array

We use a min heap to maintain the n largest elements, processing from right to left:

// rightMaxs[i] = maximum sum of n elements from nums[i...3n-1]
for (int i = 3*n - 1; i >= n; i--) {
    minHeap.offer(nums[i]);
    sum += nums[i];

    if (minHeap.size() > n) {
        sum -= minHeap.poll();  // Remove smallest element
    }

    if (minHeap.size() == n) {
        rightMaxs[i] = sum;
    }
}

Why min heap? We want to keep the n largest elements. When we encounter a new element larger than our current smallest large element, we replace the smallest.

Step 3: Find Minimum Difference

long result = Long.MAX_VALUE;
for (int i = n - 1; i < 2 * n; i++) {
    result = Math.min(result, leftMins[i] - rightMaxs[i + 1]);
}

Complete Solution

class Solution {
    public long minimumDifference(int[] nums) {
        int n = nums.length / 3;

        long[] leftMins = new long[nums.length];
        long[] rightMaxs = new long[nums.length];

        // Build leftMins: minimum sum of n elements from left
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a,b) -> b-a);
        long sum = 0;

        for (int i = 0; i < 2 * n; i++) {
            maxHeap.offer(nums[i]);
            sum += nums[i];

            if (maxHeap.size() > n) {
                sum -= maxHeap.poll();
            }

            if (maxHeap.size() == n) {
                leftMins[i] = sum;
            }
        }

        // Build rightMaxs: maximum sum of n elements from right
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        sum = 0;

        for (int i = nums.length - 1; i >= n; i--) {
            minHeap.offer(nums[i]);
            sum += nums[i];

            if (minHeap.size() > n) {
                sum -= minHeap.poll();
            }

            if (minHeap.size() == n) {
                rightMaxs[i] = sum;
            }
        }

        // Find minimum difference
        long result = Long.MAX_VALUE;
        for (int i = n - 1; i < 2 * n; i++) {
            result = Math.min(result, leftMins[i] - rightMaxs[i + 1]);
        }

        return result;
    }
}

Example Trace

For nums = [7,9,5,8,1,3] where n = 2:

Building leftMins:

  • i=0: heap=[7], sum=7
  • i=1: heap=[9,7], sum=16, leftMins[1]=16
  • i=2: heap=[7,5], sum=12, leftMins[2]=12 (replaced 9 with 5)
  • i=3: heap=[7,5], sum=12, leftMins[3]=12 (8 > 7, no change)

Building rightMaxs:

  • i=5: heap=[3], sum=3
  • i=4: heap=[3,1], sum=4, rightMaxs[4]=4
  • i=3: heap=[8,3], sum=11, rightMaxs[3]=11 (replaced 1 with 8)
  • i=2: heap=[8,5], sum=13, rightMaxs[2]=13 (replaced 3 with 5)

Finding minimum difference:

  • i=1: leftMins[1] - rightMaxs[2] = 16 - 13 = 3
  • i=2: leftMins[2] - rightMaxs[3] = 12 - 11 = 1
  • i=3: leftMins[3] - rightMaxs[4] = 12 - 4 = 8

Result: 1

Complexity Analysis

Time Complexity: O(n log n)

Breakdown:

  • Building leftMins: O(n log n)

    • Loop runs for 2n iterations
    • Each heap operation (offer/poll) takes O(log n)
    • Total: O(2n × log n) = O(n log n)
  • Building rightMaxs: O(n log n)

    • Loop runs for 2n iterations
    • Each heap operation takes O(log n)
    • Total: O(2n × log n) = O(n log n)
  • Finding minimum: O(n)

    • Single loop through n elements
    • Constant time operations inside

Overall: O(n log n) + O(n log n) + O(n) = O(n log n)

Space Complexity: O(n)

Breakdown:

  • Arrays: leftMins and rightMaxs each use O(3n) = O(n) space
  • Heaps: Each heap stores at most n elements, so O(n) space each
  • Variables: Constant space for sum, result, etc.

Overall: O(n) + O(n) + O(n) = O(n)

Why This Approach is Optimal

  1. Greedy Strategy: At each position, we optimally choose the best n elements available
  2. No Redundant Work: Each element is processed exactly once in each direction
  3. Efficient Data Structures: Heaps allow us to maintain the best n elements efficiently
  4. Complete Coverage: We consider all possible valid split points

This solution efficiently handles the constraint that removed elements can be non-contiguous while maintaining the requirement that remaining elements preserve their relative order.

0
Subscribe to my newsletter

Read articles from Anni Huang directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Anni Huang
Anni Huang

I am Anni HUANG, a software engineer with 3 years of experience in IDE development and Chatbot.