Facebook Pixel

3107. Minimum Operations to Make Median of Array Equal to K

Problem Description

You have an integer array nums and a non-negative integer k. Your goal is to make the median of the array equal to k by performing operations. In each operation, you can increase or decrease any element by 1.

The median is defined as the middle element when the array is sorted in non-decreasing order. For arrays with an even number of elements, the median is the larger of the two middle values.

You need to find the minimum number of operations required to achieve a median of k.

For example, if you have nums = [1, 2, 3, 4, 5] and k = 3, the median is already 3, so you need 0 operations. But if k = 6, you would need to increase some elements to make the median equal to 6.

The strategy involves sorting the array first to identify the median position. Then:

  • If the current median is greater than k, you need to decrease it to k and also ensure elements to its left that are greater than k are decreased to k (to maintain the median property)
  • If the current median is less than k, you need to increase it to k and also ensure elements to its right that are less than k are increased to k (to maintain the median property)

The solution counts the total number of unit changes needed across all affected elements to achieve the target median of k.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is understanding what happens to the median when we modify array elements. After sorting the array, the median is at position m = n // 2. For the median to remain at this position and have value k, we need to maintain a specific order relationship.

Think about it this way: if we want the median to be k, then:

  • All elements to the left of the median position should be โ‰ค k
  • All elements to the right of the median position should be โ‰ฅ k

Why? Because if any element on the left is greater than k, when we sort the array, that larger value might shift to the median position or push our desired k value away from the median position. Similarly, if any element on the right is less than k, it could interfere with k being the median.

This leads to our greedy approach:

  1. First, change the current median to k - this costs |nums[m] - k| operations
  2. If the original median was greater than k, we need to check leftward. Any element to the left that's still greater than k must be reduced to exactly k to prevent it from becoming the new median
  3. If the original median was less than or equal to k, we need to check rightward. Any element to the right that's still less than k must be increased to exactly k

The brilliance of this approach is that we don't need to over-adjust any element. Making elements on the left exactly k (not less) and elements on the right exactly k (not more) is optimal because it uses the minimum operations while guaranteeing k stays as the median.

Learn more about Greedy and Sorting patterns.

Solution Approach

The implementation follows a greedy approach with sorting:

  1. Sort the array: First, we sort nums to easily identify the median position. After sorting, the median is at index m = n >> 1 (equivalent to n // 2).

  2. Calculate initial operations: We start by calculating the operations needed to change the current median to k: ans = abs(nums[m] - k).

  3. Handle two cases based on median value:

    Case 1: When nums[m] > k

    • We need to ensure all elements to the left of position m are at most k
    • Iterate from position m - 1 down to 0:
      • If nums[i] <= k, we can stop (all remaining elements to the left are already โ‰ค k due to sorting)
      • Otherwise, add nums[i] - k to our answer (operations needed to reduce nums[i] to k)

    Case 2: When nums[m] <= k

    • We need to ensure all elements to the right of position m are at least k
    • Iterate from position m + 1 to n - 1:
      • If nums[i] >= k, we can stop (all remaining elements to the right are already โ‰ฅ k due to sorting)
      • Otherwise, add k - nums[i] to our answer (operations needed to increase nums[i] to k)

The algorithm's efficiency comes from:

  • Only modifying elements that violate the median constraint
  • Using early termination when we encounter elements that already satisfy the condition
  • Leveraging the sorted order to know that once we find a valid element, all elements beyond it in that direction are also valid

Time complexity: O(n log n) due to sorting, where n is the length of the array. Space complexity: O(1) if we don't count the sorting space, otherwise O(log n) for the sorting algorithm's recursion stack.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through an example with nums = [2, 5, 6, 8, 9] and k = 4.

Step 1: Sort the array The array is already sorted: [2, 5, 6, 8, 9]

Step 2: Find the median position With n = 5, the median position is m = 5 // 2 = 2 Current median value: nums[2] = 6

Step 3: Calculate initial operations We need to change the median from 6 to 4:

  • Operations needed: |6 - 4| = 2
  • Running total: ans = 2

Step 4: Determine which direction to check Since nums[m] = 6 > k = 4, we need to check elements to the left.

Step 5: Check and adjust elements to the left We want all elements left of position 2 to be โ‰ค 4:

  • Position 1: nums[1] = 5
    • Since 5 > 4, we need to decrease it to 4
    • Operations: 5 - 4 = 1
    • Running total: ans = 2 + 1 = 3
  • Position 0: nums[0] = 2
    • Since 2 โ‰ค 4, we stop here (no adjustment needed)

Final Result After 3 operations, our array becomes [2, 4, 4, 8, 9]:

  • Changed position 2 from 6 to 4 (2 operations)
  • Changed position 1 from 5 to 4 (1 operation)

When sorted, this array has median = 4, which equals our target k.

The key insight: We only needed to adjust elements that violated the median constraint (elements on the left that were greater than k). Elements on the right (8 and 9) didn't need adjustment because they're already โ‰ฅ k, which is what we want for elements right of the median.

Solution Implementation

1class Solution:
2    def minOperationsToMakeMedianK(self, nums: List[int], k: int) -> int:
3        # Sort the array to find the median position
4        nums.sort()
5        n = len(nums)
6      
7        # Find the median index (middle element for odd-length arrays)
8        median_index = n // 2
9      
10        # Initialize answer with the cost to change median to k
11        total_operations = abs(nums[median_index] - k)
12      
13        # If current median is greater than k, we need to decrease it
14        # All elements to the left of median should be at most k
15        if nums[median_index] > k:
16            # Check elements to the left of median (in reverse order)
17            for i in range(median_index - 1, -1, -1):
18                # If element is already <= k, no need to continue
19                if nums[i] <= k:
20                    break
21                # Add cost to decrease this element to k
22                total_operations += nums[i] - k
23        else:
24            # If current median is less than or equal to k
25            # All elements to the right of median should be at least k
26            for i in range(median_index + 1, n):
27                # If element is already >= k, no need to continue
28                if nums[i] >= k:
29                    break
30                # Add cost to increase this element to k
31                total_operations += k - nums[i]
32      
33        return total_operations
34
1class Solution {
2    public long minOperationsToMakeMedianK(int[] nums, int k) {
3        // Sort the array to find the median position
4        Arrays.sort(nums);
5      
6        // Get array length and calculate median index
7        int n = nums.length;
8        int medianIndex = n / 2;  // For odd length, this is the middle; for even, this is the upper middle
9      
10        // Start with the cost to change the median element itself to k
11        long totalOperations = Math.abs(nums[medianIndex] - k);
12      
13        // If current median is greater than target k
14        if (nums[medianIndex] > k) {
15            // All elements to the left of median that are greater than k need to be reduced to k
16            // This ensures they remain <= median after median becomes k
17            for (int i = medianIndex - 1; i >= 0 && nums[i] > k; i--) {
18                totalOperations += nums[i] - k;
19            }
20        } else {
21            // If current median is less than or equal to target k
22            // All elements to the right of median that are less than k need to be increased to k
23            // This ensures they remain >= median after median becomes k
24            for (int i = medianIndex + 1; i < n && nums[i] < k; i++) {
25                totalOperations += k - nums[i];
26            }
27        }
28      
29        return totalOperations;
30    }
31}
32
1class Solution {
2public:
3    long long minOperationsToMakeMedianK(vector<int>& nums, int k) {
4        // Sort the array to find the median element
5        sort(nums.begin(), nums.end());
6      
7        // Get array size and calculate median index
8        int n = nums.size();
9        int medianIndex = n / 2;  // For odd-sized array, this gives the middle element
10      
11        // Initialize result with operations needed to change median to k
12        long long totalOperations = abs(nums[medianIndex] - k);
13      
14        // If current median is greater than target k
15        if (nums[medianIndex] > k) {
16            // All elements to the left of median that are greater than k
17            // must be reduced to k (to maintain sorted order)
18            for (int i = medianIndex - 1; i >= 0 && nums[i] > k; --i) {
19                totalOperations += nums[i] - k;
20            }
21        } 
22        // If current median is less than or equal to target k
23        else {
24            // All elements to the right of median that are less than k
25            // must be increased to k (to maintain sorted order)
26            for (int i = medianIndex + 1; i < n && nums[i] < k; ++i) {
27                totalOperations += k - nums[i];
28            }
29        }
30      
31        return totalOperations;
32    }
33};
34
1/**
2 * Calculates the minimum number of operations to make the median of an array equal to k.
3 * Each operation allows incrementing or decrementing an element by 1.
4 * 
5 * @param nums - The input array of numbers
6 * @param k - The target median value
7 * @returns The minimum number of operations required
8 */
9function minOperationsToMakeMedianK(nums: number[], k: number): number {
10    // Sort the array in ascending order
11    nums.sort((a: number, b: number) => a - b);
12  
13    // Get array length and calculate median index
14    const arrayLength: number = nums.length;
15    const medianIndex: number = arrayLength >> 1; // Equivalent to Math.floor(arrayLength / 2)
16  
17    // Start with the operations needed to change the median element to k
18    let totalOperations: number = Math.abs(nums[medianIndex] - k);
19  
20    if (nums[medianIndex] > k) {
21        // If median is greater than k, we need to decrease it
22        // Also need to ensure all elements to the left of median are <= k
23        for (let i: number = medianIndex - 1; i >= 0 && nums[i] > k; i--) {
24            totalOperations += nums[i] - k;
25        }
26    } else {
27        // If median is less than or equal to k, we need to increase it
28        // Also need to ensure all elements to the right of median are >= k
29        for (let i: number = medianIndex + 1; i < arrayLength && nums[i] < k; i++) {
30            totalOperations += k - nums[i];
31        }
32    }
33  
34    return totalOperations;
35}
36

Time and Space Complexity

Time Complexity: O(n ร— log n)

The time complexity is dominated by the sorting operation nums.sort(), which takes O(n ร— log n) time where n is the length of the array. After sorting, the algorithm performs a single pass through at most half of the array (either from index m-1 to 0 or from index m+1 to n-1), which takes O(n) time in the worst case. Since O(n ร— log n) + O(n) = O(n ร— log n), the overall time complexity is O(n ร— log n).

Space Complexity: O(log n)

The space complexity comes from the sorting algorithm. Python's sort() method uses Timsort, which requires O(log n) space for its recursive call stack in the worst case. The algorithm itself only uses a constant amount of extra space for variables like n, m, and ans, which is O(1). Therefore, the total space complexity is O(log n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Misunderstanding the Median Definition for Even-Length Arrays

A critical pitfall is incorrectly handling the median for even-length arrays. The problem states that for even-length arrays, the median is the larger of the two middle values (upper median), not the average or the smaller value.

Incorrect assumption:

# Wrong: Using lower median for even-length arrays
median_index = (n - 1) // 2  # This gives the lower middle element

# Wrong: Using average of two middle elements
median_index = n // 2
median_value = (nums[median_index - 1] + nums[median_index]) / 2

Correct approach:

# Correct: For both odd and even length arrays
median_index = n // 2
# For odd length: this gives the middle element
# For even length: this gives the upper middle element (larger of the two)

2. Not Maintaining the Sorted Order Property

Another pitfall is only changing the median element without considering that after operations, the array must still have k as its median when sorted. Simply changing the median position element to k doesn't guarantee it remains the median.

Incorrect approach:

def minOperationsToMakeMedianK(self, nums: List[int], k: int) -> int:
    nums.sort()
    median_index = len(nums) // 2
    # Wrong: Only changing the median element
    return abs(nums[median_index] - k)

Why this fails: If nums = [1, 2, 5, 6, 7] and k = 3, just changing 5 to 3 gives [1, 2, 3, 6, 7], which is correct. But if nums = [1, 4, 5, 6, 7] and k = 2, changing 5 to 2 gives [1, 4, 2, 6, 7]. When re-sorted, this becomes [1, 2, 4, 6, 7], and the median is 4, not 2!

Correct approach: Ensure all elements to the left of the median position are โ‰ค k, and all elements to the right are โ‰ฅ k:

# After setting median to k, fix neighboring elements
if nums[median_index] > k:
    # Fix left side - make all elements > k equal to k
    for i in range(median_index - 1, -1, -1):
        if nums[i] <= k:
            break
        total_operations += nums[i] - k
else:
    # Fix right side - make all elements < k equal to k  
    for i in range(median_index + 1, n):
        if nums[i] >= k:
            break
        total_operations += k - nums[i]

3. Modifying the Original Array During Iteration

While the current solution doesn't actually modify the array (it only calculates operations), a common mistake when implementing similar problems is to modify array elements while iterating, which can lead to incorrect calculations.

Incorrect pattern:

# Wrong: Modifying array while calculating
for i in range(median_index - 1, -1, -1):
    if nums[i] > k:
        total_operations += nums[i] - k
        nums[i] = k  # Modifying the array - not needed!

Correct approach: Just calculate the operations without modifying:

# Correct: Only calculate operations
for i in range(median_index - 1, -1, -1):
    if nums[i] <= k:
        break
    total_operations += nums[i] - k
    # No modification needed - we're just counting operations
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More