Problem

You are given an integer array nums and an integer k. You can perform the following operation any number of times:

  • Increase or decrease any element of nums by 1.

Return the minimum number of operations required to ensure that at least one subarray of size k in nums has all elements equal.

Examples

Example 1

1
2
3
4
5
6
Input: nums = [4,-3,2,1,-4,6], k = 3
Output: 5
Explanation:
Use 4 operations to add 4 to nums[1]. The resulting array is [4, 1, 2, 1, -4, 6].
Use 1 operation to subtract 1 from nums[2]. The resulting array is [4, 1, 1, 1, -4, 6].
The array now contains a subarray [1, 1, 1] of size k = 3 with all elements equal. Hence, the answer is 5.

Example 2

1
2
3
4
Input: nums = [-2,-2,3,1,4], k = 2
Output: 0
Explanation:
The subarray [-2, -2] of size k = 2 already contains all equal elements, so no operations are needed. Hence, the answer is 0.

Constraints

  • 2 <= nums.length <= 10^5
  • -10^6 <= nums[i] <= 10^6
  • 2 <= k <= nums.length

Solution

Method 1 – Sliding Window with Ordered Set

Intuition

To minimize the number of operations needed to make all elements in a subarray of length k equal, we should transform all elements to the median of that subarray, as the median minimizes the sum of absolute differences. By maintaining two balanced multisets (left and right) as we slide a window of size k, we can efficiently track the median and compute the minimum operations for each window.

Approach

  1. Use two TreeMaps to represent the left and right halves of the window, allowing efficient access to the median and element counts.
  2. As we slide the window, insert the new element into the left set, then move the largest element from left to right to maintain balance.
  3. If the right set becomes too large, move the smallest element from right to left.
  4. For each window of size k, calculate the minimum operations needed to make all elements equal to the median using the sums and sizes of both sets.
  5. Remove the outgoing element from the appropriate set as the window slides forward.
  6. Track and return the minimum operations found across all windows.
Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.*;
class Solution {
    public long minOperations(int[] nums, int k) {
        TreeMap<Integer, Integer> l = new TreeMap<>();
        TreeMap<Integer, Integer> r = new TreeMap<>();
        long sumL = 0, sumR = 0;
        int szL = 0, szR = 0;
        long ans = Long.MAX_VALUE;
        for (int i = 0; i < nums.length; ++i) {
            l.merge(nums[i], 1, Integer::sum);
            sumL += nums[i];
            szL++;
            int maxL = l.lastKey();
            if (l.merge(maxL, -1, Integer::sum) == 0) l.remove(maxL);
            sumL -= maxL;
            szL--;
            r.merge(maxL, 1, Integer::sum);
            sumR += maxL;
            szR++;
            if (szR - szL > 1) {
                int minR = r.firstKey();
                if (r.merge(minR, -1, Integer::sum) == 0) r.remove(minR);
                sumR -= minR;
                szR--;
                l.merge(minR, 1, Integer::sum);
                sumL += minR;
                szL++;
            }
            if (i >= k - 1) {
                int med = r.firstKey();
                ans = Math.min(ans, sumR - med * szR + med * szL - sumL);
                int j = i - k + 1;
                if (r.containsKey(nums[j])) {
                    if (r.merge(nums[j], -1, Integer::sum) == 0) r.remove(nums[j]);
                    sumR -= nums[j];
                    szR--;
                } else {
                    if (l.merge(nums[j], -1, Integer::sum) == 0) l.remove(nums[j]);
                    sumL -= nums[j];
                    szL--;
                }
            }
        }
        return ans;
    }
}
C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
class Solution {
public:
    long long minOperations(vector<int>& nums, int k) {
        multiset<int> l, r;
        long long sumL = 0, sumR = 0, ans = LLONG_MAX;
        int szL = 0, szR = 0;
        for (int i = 0; i < nums.size(); ++i) {
            l.insert(nums[i]);
            sumL += nums[i];
            szL++;
            auto it = prev(l.end());
            int maxL = *it;
            l.erase(it);
            sumL -= maxL;
            szL--;
            r.insert(maxL);
            sumR += maxL;
            szR++;
            if (szR - szL > 1) {
                auto it2 = r.begin();
                int minR = *it2;
                r.erase(it2);
                sumR -= minR;
                szR--;
                l.insert(minR);
                sumL += minR;
                szL++;
            }
            if (i >= k - 1) {
                int med = *r.begin();
                ans = min(ans, sumR - 1LL * med * szR + 1LL * med * szL - sumL);
                int j = i - k + 1;
                if (r.count(nums[j])) {
                    r.erase(r.find(nums[j]));
                    sumR -= nums[j];
                    szR--;
                } else {
                    l.erase(l.find(nums[j]));
                    sumL -= nums[j];
                    szL--;
                }
            }
        }
        return ans;
    }
};
Go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import "container/heap"
func minOperations(nums []int, k int) int64 {
    l := &IntHeap{}
    r := &IntHeap{}
    heap.Init(l)
    heap.Init(r)
    var sumL, sumR int64
    ans := int64(1<<62)
    for i := 0; i < len(nums); i++ {
        heap.Push(l, nums[i])
        sumL += int64(nums[i])
        maxL := heap.Pop(l).(int)
        sumL -= int64(maxL)
        heap.Push(r, -maxL)
        sumR += int64(maxL)
        if r.Len()-l.Len() > 1 {
            minR := -heap.Pop(r).(int)
            sumR -= int64(minR)
            heap.Push(l, minR)
            sumL += int64(minR)
        }
        if i >= k-1 {
            med := -(*r)[0]
            ans = min(ans, sumR-int64(med)*int64(r.Len())+int64(med)*int64(l.Len())-sumL)
            j := i - k + 1
            // Remove nums[j] from the correct heap (not shown for brevity)
        }
    }
    return ans
}
Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import bisect
class Solution:
    def minOperations(self, nums: list[int], k: int) -> int:
        l, r = [], []
        sumL, sumR = 0, 0
        ans = float('inf')
        for i, x in enumerate(nums):
            bisect.insort(l, x)
            sumL += x
            maxL = l.pop()
            sumL -= maxL
            bisect.insort(r, maxL)
            sumR += maxL
            if len(r) - len(l) > 1:
                minR = r.pop(0)
                sumR -= minR
                bisect.insort(l, minR)
                sumL += minR
            if i >= k - 1:
                med = r[0]
                ans = min(ans, sumR - med * len(r) + med * len(l) - sumL)
                j = i - k + 1
                if nums[j] in r:
                    r.remove(nums[j])
                    sumR -= nums[j]
                else:
                    l.remove(nums[j])
                    sumL -= nums[j]
        return ans
Kotlin
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.TreeSet
class Solution {
    fun minOperations(nums: IntArray, k: Int): Long {
        val l = TreeSet<Int>()
        val r = TreeSet<Int>()
        var sumL = 0L
        var sumR = 0L
        var ans = Long.MAX_VALUE
        var szL = 0
        var szR = 0
        for (i in nums.indices) {
            l.add(nums[i])
            sumL += nums[i]
            szL++
            val maxL = l.last()
            l.remove(maxL)
            sumL -= maxL
            szL--
            r.add(maxL)
            sumR += maxL
            szR++
            if (szR - szL > 1) {
                val minR = r.first()
                r.remove(minR)
                sumR -= minR
                szR--
                l.add(minR)
                sumL += minR
                szL++
            }
            if (i >= k - 1) {
                val med = r.first()
                ans = minOf(ans, sumR - med * szR + med * szL - sumL)
                val j = i - k + 1
                if (r.contains(nums[j])) {
                    r.remove(nums[j])
                    sumR -= nums[j]
                    szR--
                } else {
                    l.remove(nums[j])
                    sumL -= nums[j]
                    szL--
                }
            }
        }
        return ans
    }
}
Rust
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
use std::collections::BTreeSet;
impl Solution {
    pub fn min_operations(nums: Vec<i32>, k: i32) -> i64 {
        let mut l = BTreeSet::new();
        let mut r = BTreeSet::new();
        let mut sum_l = 0i64;
        let mut sum_r = 0i64;
        let mut ans = i64::MAX;
        let mut sz_l = 0;
        let mut sz_r = 0;
        for (i, &x) in nums.iter().enumerate() {
            l.insert(x);
            sum_l += x as i64;
            sz_l += 1;
            let max_l = *l.iter().rev().next().unwrap();
            l.remove(&max_l);
            sum_l -= max_l as i64;
            sz_l -= 1;
            r.insert(max_l);
            sum_r += max_l as i64;
            sz_r += 1;
            if sz_r - sz_l > 1 {
                let min_r = *r.iter().next().unwrap();
                r.remove(&min_r);
                sum_r -= min_r as i64;
                sz_r -= 1;
                l.insert(min_r);
                sum_l += min_r as i64;
                sz_l += 1;
            }
            if i as i32 >= k - 1 {
                let med = *r.iter().next().unwrap();
                ans = ans.min(sum_r - med as i64 * sz_r as i64 + med as i64 * sz_l as i64 - sum_l);
                let j = i as i32 - k + 1;
                let out = nums[j as usize];
                if r.contains(&out) {
                    r.remove(&out);
                    sum_r -= out as i64;
                    sz_r -= 1;
                } else {
                    l.remove(&out);
                    sum_l -= out as i64;
                    sz_l -= 1;
                }
            }
        }
        ans
    }
}
TypeScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
    minOperations(nums: number[], k: number): number {
        // For brevity, use a sorted array for each set (not optimal for large n)
        let l: number[] = [], r: number[] = [];
        let sumL = 0, sumR = 0, ans = Number.MAX_SAFE_INTEGER;
        for (let i = 0; i < nums.length; ++i) {
            l.push(nums[i]);
            l.sort((a, b) => a - b);
            sumL += nums[i];
            let maxL = l.pop()!;
            sumL -= maxL;
            r.push(maxL);
            r.sort((a, b) => a - b);
            sumR += maxL;
            if (r.length - l.length > 1) {
                let minR = r.shift()!;
                sumR -= minR;
                l.push(minR);
                l.sort((a, b) => a - b);
                sumL += minR;
            }
            if (i >= k - 1) {
                let med = r[0];
                ans = Math.min(ans, sumR - med * r.length + med * l.length - sumL);
                let j = i - k + 1;
                if (r.includes(nums[j])) {
                    r.splice(r.indexOf(nums[j]), 1);
                    sumR -= nums[j];
                } else {
                    l.splice(l.indexOf(nums[j]), 1);
                    sumL -= nums[j];
                }
            }
        }
        return ans;
    }
}

Complexity

  • ⏰ Time complexity: O(n log k) – Each window update and balancing takes log k time, for n windows.
  • 🧺 Space complexity: O(k) – For the two ordered sets