Facebook Pixel

3576. Transform Array to All Equal Elements

Problem Description

You have an array nums of size n that contains only values of 1 and -1. You're also given an integer k representing the maximum number of operations you can perform.

In each operation, you can:

  • Select any index i where 0 <= i < n - 1
  • Multiply both nums[i] and nums[i + 1] by -1 (this flips their signs)

You can use the same index i multiple times across different operations.

The goal is to determine if it's possible to make all elements in the array equal after performing at most k operations. Return true if this is achievable, false otherwise.

For example, if you have nums = [1, -1, 1] and perform an operation at index 0, both nums[0] and nums[1] get multiplied by -1, resulting in [-1, 1, 1].

The key insight is that since the array only contains 1 and -1, making all elements equal means either all elements become 1 or all elements become -1. Each operation affects exactly two adjacent elements by flipping their signs simultaneously.

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

Intuition

Since the array only contains 1 and -1, and we want all elements to be equal, the final array must contain all 1s or all -1s. This means our target value can only be either 1 or -1.

Let's think about what happens when we perform operations. Each operation flips the signs of two adjacent elements. If we want to transform the array into all identical values, we need to consider how the operations propagate through the array.

A key observation is that we can process the array from left to right. For each position, we can determine whether we need to perform an operation based on whether the current element matches our target. If it doesn't match, we must perform an operation at this position to flip it (along with the next element).

The clever part is tracking the cumulative effect of operations. When we perform an operation at index i, it affects both nums[i] and nums[i+1]. So as we move through the array, we need to keep track of whether the current position has been affected by a previous operation.

We can use a sign variable to track this cumulative effect:

  • If sign = 1, the current element hasn't been flipped by previous operations
  • If sign = -1, the current element has been flipped once by a previous operation

When we check if nums[i] * sign equals our target:

  • If yes, no operation needed at this position, keep sign = 1 for the next element
  • If no, we need an operation here, so set sign = -1 (the next element will be flipped)

Since there are only two possible target values (nums[0] or -nums[0]), we check both possibilities and see if either can be achieved with at most k operations.

Learn more about Greedy patterns.

Solution Approach

The solution implements a helper function check(target, k) that determines if we can transform the array so all elements equal target using at most k operations.

The function works by traversing the array from left to right with two key variables:

  • cnt: counts the number of operations performed
  • sign: tracks the cumulative effect of operations (1 if no flip, -1 if flipped)

Here's how the algorithm processes each element:

  1. For each index i from 0 to n-2:

    • Calculate the effective value: x = nums[i] * sign
    • This accounts for any flips from previous operations
  2. Decision making:

    • If x == target: The current element matches our goal
      • Set sign = 1 (no operation needed, next element unaffected)
    • If x != target: The current element doesn't match
      • Set sign = -1 (perform operation, next element will be flipped)
      • Increment cnt by 1
  3. Final check:

    • After processing all pairs, verify the last element: nums[-1] * sign == target
    • Return true if both conditions are met:
      • cnt <= k (didn't exceed operation limit)
      • Last element matches the target after all operations

The main function calls check twice:

  • check(nums[0], k): Try to make all elements equal to the first element
  • check(-nums[0], k): Try to make all elements equal to the negative of the first element

The solution returns true if either attempt succeeds.

Time Complexity: O(n) where n is the length of the array, as we traverse the array at most twice.

Space Complexity: O(1) as we only use a constant amount of extra space.

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 nums = [1, -1, -1, 1] and k = 2.

We'll try two target values: 1 (same as nums[0]) and -1 (negative of nums[0]).

Attempt 1: Target = 1

Starting with cnt = 0, sign = 1:

  • Index 0:

    • Effective value: nums[0] * sign = 1 * 1 = 1
    • Compare with target: 1 == 1 βœ“
    • No operation needed, set sign = 1
  • Index 1:

    • Effective value: nums[1] * sign = -1 * 1 = -1
    • Compare with target: -1 != 1 βœ—
    • Operation needed! Increment cnt = 1, set sign = -1
    • This flips both nums[1] and nums[2]
  • Index 2:

    • Effective value: nums[2] * sign = -1 * -1 = 1
    • Compare with target: 1 == 1 βœ“
    • No operation needed, set sign = 1
  • Final check for last element:

    • nums[3] * sign = 1 * 1 = 1
    • Matches target βœ“

Result: cnt = 1 ≀ k = 2 and last element matches. Success!

The array transformation would be:

  • Initial: [1, -1, -1, 1]
  • After operation at index 1: [1, 1, 1, 1]

Attempt 2: Target = -1

Starting with cnt = 0, sign = 1:

  • Index 0:

    • Effective value: nums[0] * sign = 1 * 1 = 1
    • Compare with target: 1 != -1 βœ—
    • Operation needed! Increment cnt = 1, set sign = -1
  • Index 1:

    • Effective value: nums[1] * sign = -1 * -1 = 1
    • Compare with target: 1 != -1 βœ—
    • Operation needed! Increment cnt = 2, set sign = -1
  • Index 2:

    • Effective value: nums[2] * sign = -1 * -1 = 1
    • Compare with target: 1 != -1 βœ—
    • Operation needed! Increment cnt = 3, set sign = -1
  • Final check:

    • cnt = 3 > k = 2 Failed!

Since the first attempt succeeded with only 1 operation, the function returns true.

Solution Implementation

1class Solution:
2    def canMakeEqual(self, nums: List[int], k: int) -> bool:
3        """
4        Check if we can make all elements equal by flipping signs of subarrays.
5      
6        Args:
7            nums: List of integers
8            k: Maximum number of operations allowed
9          
10        Returns:
11            True if possible within k operations, False otherwise
12        """
13      
14        def can_achieve_target(target_value: int, max_operations: int) -> bool:
15            """
16            Check if all elements can be made equal to target_value.
17          
18            Strategy: Traverse array and flip sign whenever current element 
19            doesn't match the target (considering accumulated sign flips).
20          
21            Args:
22                target_value: The value we want all elements to become
23                max_operations: Maximum allowed sign flip operations
24              
25            Returns:
26                True if achievable within max_operations
27            """
28            flip_count = 0  # Number of sign flip operations performed
29            current_sign = 1  # Current sign multiplier (1 or -1)
30          
31            # Process all elements except the last one
32            for i in range(len(nums) - 1):
33                # Apply current sign to get effective value
34                effective_value = nums[i] * current_sign
35              
36                if effective_value == target_value:
37                    # Value matches target, no flip needed for next element
38                    current_sign = 1
39                else:
40                    # Value doesn't match, need to flip sign for next element
41                    current_sign = -1
42                    flip_count += 1
43          
44            # Check if last element matches target and flip count is within limit
45            last_element_matches = (nums[-1] * current_sign == target_value)
46            within_operation_limit = (flip_count <= max_operations)
47          
48            return last_element_matches and within_operation_limit
49      
50        # Try both possible target values: first element or its negative
51        positive_target_works = can_achieve_target(nums[0], k)
52        negative_target_works = can_achieve_target(-nums[0], k)
53      
54        return positive_target_works or negative_target_works
55
1class Solution {
2    /**
3     * Determines if all elements in the array can be made equal by applying
4     * at most k operations. Each operation multiplies a contiguous subarray by -1.
5     * 
6     * @param nums the input array
7     * @param k maximum number of operations allowed
8     * @return true if all elements can be made equal, false otherwise
9     */
10    public boolean canMakeEqual(int[] nums, int k) {
11        // Try two possible target values: positive and negative of first element
12        return canTransformToTarget(nums, nums[0], k) || 
13               canTransformToTarget(nums, -nums[0], k);
14    }
15
16    /**
17     * Checks if all elements can be transformed to the target value
18     * using at most k sign-flip operations on contiguous subarrays.
19     * 
20     * @param nums the input array
21     * @param targetValue the target value to make all elements equal to
22     * @param maxOperations maximum number of operations allowed
23     * @return true if transformation is possible, false otherwise
24     */
25    private boolean canTransformToTarget(int[] nums, int targetValue, int maxOperations) {
26        int operationCount = 0;
27        int currentSign = 1;  // Tracks whether current segment needs sign flip
28      
29        // Process all elements except the last one
30        for (int i = 0; i < nums.length - 1; i++) {
31            int currentValue = nums[i] * currentSign;
32          
33            if (currentValue == targetValue) {
34                // Current element matches target, no flip needed for next element
35                currentSign = 1;
36            } else {
37                // Current element doesn't match, need to flip sign for next segment
38                currentSign = -1;
39                operationCount++;
40            }
41        }
42      
43        // Check if last element matches target and operation count is within limit
44        int lastElement = nums[nums.length - 1] * currentSign;
45        return operationCount <= maxOperations && lastElement == targetValue;
46    }
47}
48
1class Solution {
2public:
3    bool canMakeEqual(vector<int>& nums, int k) {
4        // Lambda function to check if we can make all elements equal to target value
5        // using at most k prefix negation operations
6        auto canReachTarget = [&](int targetValue, int maxOperations) -> bool {
7            int arraySize = nums.size();
8            int operationCount = 0;  // Count of prefix negations performed
9            int currentSign = 1;      // Current sign multiplier (1 or -1)
10          
11            // Process all elements except the last one
12            for (int i = 0; i < arraySize - 1; ++i) {
13                // Calculate the current value after applying accumulated sign changes
14                int currentValue = nums[i] * currentSign;
15              
16                if (currentValue == targetValue) {
17                    // Current element matches target, no need to negate prefix
18                    currentSign = 1;
19                } else {
20                    // Current element doesn't match, need to negate prefix
21                    currentSign = -1;
22                    ++operationCount;
23                }
24            }
25          
26            // Check if:
27            // 1. We haven't exceeded the operation limit
28            // 2. The last element (with accumulated sign) equals the target
29            return operationCount <= maxOperations && 
30                   nums[arraySize - 1] * currentSign == targetValue;
31        };
32      
33        // Try both possible target values:
34        // 1. Original value of first element
35        // 2. Negated value of first element
36        return canReachTarget(nums[0], k) || canReachTarget(-nums[0], k);
37    }
38};
39
1/**
2 * Determines if we can make all elements equal by flipping signs of subarrays
3 * @param nums - The input array of numbers
4 * @param k - Maximum number of operations allowed
5 * @returns true if all elements can be made equal, false otherwise
6 */
7function canMakeEqual(nums: number[], k: number): boolean {
8    /**
9     * Checks if all elements can be made equal to the target value
10     * @param target - The target value to make all elements equal to
11     * @param k - Maximum number of operations allowed
12     * @returns true if possible, false otherwise
13     */
14    function check(target: number, k: number): boolean {
15        // Track the number of sign flips and current sign multiplier
16        let operationCount: number = 0;
17        let currentSign: number = 1;
18      
19        // Process all elements except the last one
20        for (let i = 0; i < nums.length - 1; i++) {
21            // Apply current sign to the element
22            const currentValue: number = nums[i] * currentSign;
23          
24            if (currentValue === target) {
25                // Element already equals target, maintain positive sign for next element
26                currentSign = 1;
27            } else {
28                // Element doesn't equal target, flip sign for next element
29                currentSign = -1;
30                operationCount++;
31            }
32        }
33      
34        // Check if operations are within limit and last element can reach target
35        return operationCount <= k && nums[nums.length - 1] * currentSign === target;
36    }
37  
38    // Try both positive and negative values of the first element as target
39    return check(nums[0], k) || check(-nums[0], k);
40}
41

Time and Space Complexity

The time complexity is O(n), where n is the length of the array nums. The algorithm calls the check function twice (once with nums[0] and once with -nums[0]). Each call to check iterates through the array once using a for loop that runs from 0 to len(nums) - 1, performing constant-time operations in each iteration (comparisons, arithmetic operations, and variable assignments). Since we make two passes through the array with constant work per element, the overall time complexity is O(2n) = O(n).

The space complexity is O(1). The algorithm only uses a fixed number of variables (target, k, cnt, sign, i, and x) regardless of the input size. No additional data structures that grow with the input size are created. All operations are performed in-place using constant extra space.

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

Common Pitfalls

Pitfall 1: Misunderstanding the Sign Tracking Mechanism

The Problem: A common mistake is incorrectly tracking how the sign variable propagates through the array. Developers often confuse when to reset sign to 1 versus when to set it to -1, leading to incorrect counting of operations or wrong final element evaluation.

Why It Happens: The sign variable represents the cumulative effect of all previous operations on the current position. When we perform an operation at index i, it affects both nums[i] and nums[i+1]. This dual effect can be confusing:

  • If we decide to flip at position i, the next element nums[i+1] will be affected by this flip
  • The sign variable carries this effect forward to properly evaluate subsequent elements

Incorrect Implementation Example:

def can_achieve_target_wrong(target_value, max_operations):
    flip_count = 0
  
    for i in range(len(nums) - 1):
        # WRONG: Not tracking cumulative sign changes
        if nums[i] != target_value:
            nums[i] *= -1  # Modifying the array directly
            nums[i+1] *= -1
            flip_count += 1
  
    return nums[-1] == target_value and flip_count <= max_operations

The Fix: Never modify the original array. Instead, track the cumulative sign effect:

def can_achieve_target_correct(target_value, max_operations):
    flip_count = 0
    current_sign = 1  # Tracks cumulative effect
  
    for i in range(len(nums) - 1):
        effective_value = nums[i] * current_sign  # Apply cumulative effect
      
        if effective_value == target_value:
            current_sign = 1  # No operation needed, reset for next element
        else:
            current_sign = -1  # Operation performed, next element will be flipped
            flip_count += 1
  
    # Apply cumulative sign to last element for final check
    return nums[-1] * current_sign == target_value and flip_count <= max_operations

Pitfall 2: Forgetting to Check Both Target Values

The Problem: Only checking if all elements can become equal to one specific value (like always trying to make everything equal to 1 or always equal to the first element's original value).

Why It Happens: The problem asks if we can make all elements equal, but doesn't specify what that equal value should be. Since we're dealing with only 1 and -1, there are exactly two possibilities: all 1s or all -1s.

Incorrect Implementation Example:

def canMakeEqual_wrong(nums, k):
    # WRONG: Only checking if we can make everything equal to 1
    return can_achieve_target(1, k)

The Fix: Always check both possibilities - making all elements equal to nums[0] or -nums[0]:

def canMakeEqual_correct(nums, k):
    # Check both possible target values
    return can_achieve_target(nums[0], k) or can_achieve_target(-nums[0], k)

Pitfall 3: Off-by-One Error in Loop Range

The Problem: Iterating through all n elements instead of stopping at n-1, causing an index out of bounds error or incorrect logic.

Why It Happens: Each operation affects elements at indices i and i+1. When we're at the last element, there's no i+1 to flip with, so we must stop our iteration at n-2 (inclusive).

Incorrect Implementation Example:

def can_achieve_target_wrong(target_value, max_operations):
    flip_count = 0
    current_sign = 1
  
    # WRONG: Should be range(len(nums) - 1)
    for i in range(len(nums)):  
        effective_value = nums[i] * current_sign
        if effective_value != target_value:
            current_sign = -1
            flip_count += 1
        else:
            current_sign = 1
  
    # This logic is now incorrect as we've processed the last element in the loop
    return flip_count <= max_operations

The Fix: Ensure the loop runs from 0 to n-2 (inclusive), then handle the last element separately:

for i in range(len(nums) - 1):  # Stop before the last element
    # Process pairs (i, i+1)
    ...
# Check the last element separately after the loop
return nums[-1] * current_sign == target_value and flip_count <= max_operations
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.

Which of the following method should we use to solve this problem?


Recommended Readings

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

Load More