Facebook Pixel

3012. Minimize Length of Array Using Operations

MediumGreedyArrayMathNumber Theory
Leetcode Link

Problem Description

You have an array nums of positive integers (0-indexed). Your goal is to minimize the array's length by repeatedly performing this operation:

  1. Choose two distinct indices i and j where both nums[i] > 0 and nums[j] > 0
  2. Calculate nums[i] % nums[j] and append this result to the end of the array
  3. Remove the elements at indices i and j from the array

You can perform this operation any number of times (including zero times). Return the minimum possible length of the array after performing operations.

How the operation works:

  • Each operation takes two elements, removes them, and adds one new element (the modulo result)
  • This reduces the array size by 1 each time
  • The modulo operation nums[i] % nums[j] gives the remainder when dividing nums[i] by nums[j]

Key insights from the solution:

  • The minimum element mi in the array plays a crucial role
  • If any element is not divisible by mi, we can create an element smaller than mi through operations, which can then eliminate all other elements (resulting in length 1)
  • If all elements are divisible by mi, we can eliminate all larger elements using mi, leaving only copies of mi
  • When we have multiple copies of mi, we pair them up for operations. Each pair produces 0 (since mi % mi = 0), which gets removed in subsequent operations
  • With cnt copies of mi, we end up with ⌈cnt/2⌉ elements remaining

The solution checks if any element has a non-zero remainder when divided by the minimum element. If yes, return 1. Otherwise, return (count of minimum element + 1) // 2.

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

Intuition

The key observation is that the modulo operation a % b always produces a result smaller than b (when b > 0). This means we can potentially create smaller and smaller elements through operations.

Let's think about the smallest element mi in the array. This element is special because:

  • We cannot make mi smaller by taking mi % anything (since mi % x equals mi when x > mi)
  • But we can potentially create elements smaller than mi by taking x % mi for other elements x

This leads us to consider two scenarios:

Scenario 1: We can create an element smaller than mi

If there's any element x where x % mi ≠ 0, then 0 < x % mi < mi. This creates an element smaller than our current minimum! Once we have this smaller element, we can use it to "consume" all other elements through repeated operations, since the modulo with the smallest element will keep producing values less than or equal to itself. Eventually, we can reduce the array to just 1 element.

Scenario 2: We cannot create anything smaller than mi

This happens when every element in the array is a multiple of mi (i.e., x % mi = 0 for all x). In this case:

  • Operations with mi and larger elements produce 0 (which we can then eliminate)
  • Operations between two mi values produce 0 (mi % mi = 0)
  • We can eliminate all elements larger than mi first, leaving only copies of mi
  • With multiple copies of mi, we pair them up. Each pair becomes 0 through the operation
  • If we have an odd number of mi values, one remains unpaired

Therefore, with cnt copies of mi, we need ⌈cnt/2⌉ operations to pair them up, leaving ⌈cnt/2⌉ elements in the end.

This intuition directly translates to the solution: check if any element has a non-zero remainder with mi. If yes, return 1. Otherwise, count how many copies of mi exist and return (count + 1) // 2.

Learn more about Greedy and Math patterns.

Solution Approach

The implementation follows directly from our case analysis:

Step 1: Find the minimum element

mi = min(nums)

We first identify the smallest element in the array, which is the key value that determines our strategy.

Step 2: Check if we can create a smaller element

if any(x % mi for x in nums):
    return 1

This checks if there exists any element x in nums where x % mi ≠ 0. The expression x % mi evaluates to a non-zero value (truthy) when x is not divisible by mi. If such an element exists, we can create an element smaller than mi through operations, which can then eliminate all other elements, leaving us with a single element.

Step 3: Handle the case where all elements are multiples of mi

return (nums.count(mi) + 1) // 2

If all elements are multiples of mi, we know that:

  • We can use mi to eliminate all elements larger than mi (since larger_element % mi = 0)
  • We're left with only copies of mi
  • When we have cnt copies of mi, we pair them up for operations
  • Each pair of mi values produces 0 when operated on each other (mi % mi = 0)
  • The formula (cnt + 1) // 2 gives us ⌈cnt/2⌉, which is the ceiling division

The integer division (cnt + 1) // 2 effectively implements ceiling division:

  • If cnt is even: (cnt + 1) // 2 = cnt // 2 (all elements pair up perfectly)
  • If cnt is odd: (cnt + 1) // 2 = (cnt + 1) // 2 (one element remains unpaired)

This solution is optimal with O(n) time complexity for finding the minimum and counting occurrences, and O(1) space complexity.

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 the solution with a concrete example: nums = [6, 9, 15, 3]

Step 1: Find the minimum element

  • mi = min([6, 9, 15, 3]) = 3

Step 2: Check if we can create a smaller element

  • Check each element's remainder when divided by 3:
    • 6 % 3 = 0 (6 is divisible by 3)
    • 9 % 3 = 0 (9 is divisible by 3)
    • 15 % 3 = 0 (15 is divisible by 3)
    • 3 % 3 = 0 (3 is divisible by itself)
  • Since all remainders are 0, we cannot create an element smaller than 3

Step 3: Count occurrences of minimum and calculate result

  • Count of mi (which is 3) in the array: 1
  • Result: (1 + 1) // 2 = 2 // 2 = 1

So the minimum possible length is 1.


Another example to illustrate the other case: nums = [8, 5, 10]

Step 1: Find the minimum element

  • mi = min([8, 5, 10]) = 5

Step 2: Check if we can create a smaller element

  • Check each element's remainder when divided by 5:
    • 8 % 5 = 3 (not divisible by 5!)
    • 5 % 5 = 0 (5 is divisible by itself)
    • 10 % 5 = 0 (10 is divisible by 5)
  • Since 8 % 5 = 3, which is non-zero, we can create an element (3) smaller than our minimum (5)

Step 3: Return 1

  • Because we found a non-zero remainder, we can reduce the array to a single element
  • Result: 1

The key insight: once we have 3 (which is smaller than 5), we can use it to eventually eliminate all other elements through repeated operations, since any number modulo 3 will produce a value ≤ 3.

Solution Implementation

1class Solution:
2    def minimumArrayLength(self, nums: List[int]) -> int:
3        # Find the minimum value in the array
4        min_value = min(nums)
5      
6        # Check if any element in the array is NOT divisible by the minimum value
7        # If such an element exists, we can always reduce the array to length 1
8        if any(num % min_value != 0 for num in nums):
9            return 1
10      
11        # If all elements are divisible by the minimum value,
12        # count how many times the minimum value appears
13        # The minimum array length is ceiling of (count of minimum / 2)
14        # which is equivalent to (count + 1) // 2
15        min_count = nums.count(min_value)
16        return (min_count + 1) // 2
17
1class Solution {
2    public int minimumArrayLength(int[] nums) {
3        // Find the minimum value in the array
4        int minValue = Arrays.stream(nums).min().getAsInt();
5      
6        // Count occurrences of the minimum value
7        int minCount = 0;
8      
9        // Iterate through all elements in the array
10        for (int num : nums) {
11            // If any element is not divisible by the minimum value,
12            // we can reduce the array to length 1
13            if (num % minValue != 0) {
14                return 1;
15            }
16          
17            // Count how many times the minimum value appears
18            if (num == minValue) {
19                minCount++;
20            }
21        }
22      
23        // The minimum array length is ceiling of (count of min values / 2)
24        // This is because we can pair up minimum values to eliminate them
25        return (minCount + 1) / 2;
26    }
27}
28
1class Solution {
2public:
3    int minimumArrayLength(vector<int>& nums) {
4        // Find the minimum element in the array
5        int minValue = *min_element(nums.begin(), nums.end());
6      
7        // Count occurrences of the minimum value
8        int minCount = 0;
9      
10        // Iterate through all elements in the array
11        for (int num : nums) {
12            // If any element is not divisible by the minimum value,
13            // we can reduce the array to length 1
14            if (num % minValue != 0) {
15                return 1;
16            }
17          
18            // Count how many times the minimum value appears
19            if (num == minValue) {
20                minCount++;
21            }
22        }
23      
24        // If all elements are divisible by the minimum value,
25        // the minimum possible array length is ceil(minCount / 2)
26        // which is equivalent to (minCount + 1) / 2
27        return (minCount + 1) / 2;
28    }
29};
30
1/**
2 * Finds the minimum possible length of the array after performing operations.
3 * The operation involves selecting two elements and replacing them with their remainder.
4 * 
5 * @param nums - The input array of positive integers
6 * @returns The minimum possible array length after operations
7 */
8function minimumArrayLength(nums: number[]): number {
9    // Find the minimum value in the array
10    const minValue = Math.min(...nums);
11  
12    // Count occurrences of the minimum value
13    let minValueCount = 0;
14  
15    // Iterate through each element in the array
16    for (const currentNumber of nums) {
17        // If any number is not divisible by the minimum value,
18        // we can reduce the array to length 1
19        if (currentNumber % minValue !== 0) {
20            return 1;
21        }
22      
23        // Count how many times the minimum value appears
24        if (currentNumber === minValue) {
25            minValueCount++;
26        }
27    }
28  
29    // Return ceiling of (minValueCount / 2)
30    // Using bit shift: (count + 1) >> 1 is equivalent to Math.ceil(count / 2)
31    return (minValueCount + 1) >> 1;
32}
33

Time and Space Complexity

The time complexity is O(n), where n is the length of the array nums. This is because:

  • Finding the minimum element using min(nums) requires O(n) time to traverse all elements
  • The generator expression any(x % mi for x in nums) iterates through the array once in the worst case, taking O(n) time
  • Counting occurrences with nums.count(mi) requires another O(n) traversal
  • These operations are performed sequentially, resulting in O(n) + O(n) + O(n) = O(n) overall

The space complexity is O(1). The algorithm only uses a constant amount of extra space:

  • The variable mi stores a single integer
  • The generator in any() uses O(1) space as it processes elements one at a time without storing them
  • The count operation returns a single integer
  • No additional data structures are created that scale with input size

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

Common Pitfalls

1. Misunderstanding the Operation's Effect on Array Size

A common mistake is thinking that the operation always reduces the array size by 1. While this is true for non-zero results, when nums[i] % nums[j] = 0, the zero gets added to the array but can be removed in the next operation (since we need both elements to be positive). This confusion can lead to incorrect calculations of the final array length.

Solution: Remember that zeros resulting from operations effectively disappear since they cannot be used in subsequent operations (which require positive integers).

2. Incorrectly Handling the Case Where All Elements Equal the Minimum

Some might think that if all elements are equal to the minimum value, the answer should always be 1. However, when you have multiple copies of the same value x, performing x % x = 0 creates zeros that get eliminated, but you need pairs to do this operation.

Example: If nums = [3, 3, 3], you can't reduce it to length 1. You can only pair two 3's to get 0, leaving you with one 3, so the answer is 2, not 1.

Solution: Use the ceiling division formula (count + 1) // 2 to correctly handle both even and odd counts of the minimum value.

3. Not Recognizing the Special Role of the Minimum Element

A pitfall is attempting complex strategies without recognizing that the minimum element is the key. Some might try to find GCD of all elements or use other mathematical properties, missing the simpler insight that any element not divisible by the minimum can create an even smaller element.

Solution: Always start by finding the minimum element and check divisibility against it first. This immediately tells you whether you can achieve length 1.

4. Integer Division vs. Ceiling Division Confusion

Using regular integer division count // 2 instead of ceiling division (count + 1) // 2 will give incorrect results when the count is odd.

Example: With 5 copies of the minimum value, 5 // 2 = 2 but the correct answer is 3 (you can pair 4 of them, leaving 3 elements).

Solution: Always use (count + 1) // 2 for ceiling division in Python when working with positive integers.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What are the two properties the problem needs to have for dynamic programming to be applicable? (Select 2)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More