Facebook Pixel

2571. Minimum Operations to Reduce an Integer to 0

Problem Description

You are given a positive integer n. You can perform the following operation any number of times:

  • Add or subtract a power of 2 from n

Your goal is to return the minimum number of operations needed to make n equal to 0.

A number x is a power of 2 if x = 2^i where i >= 0 (for example: 1, 2, 4, 8, 16, ...).

The key insight is that when we look at the binary representation of n, we need to eliminate all the 1-bits. The solution uses a greedy approach by processing the binary representation from right to left:

  • When encountering isolated 1-bits (like ...010...), we can eliminate them with a single subtraction operation
  • When encountering consecutive 1-bits (like ...0111...), it's more efficient to add a power of 2 to convert them to ...1000..., which leaves us with just one 1-bit to handle later

The algorithm tracks consecutive 1-bits using a counter cnt:

  • If the current bit is 1, increment the counter
  • If the current bit is 0 and we have accumulated 1-bits:
    • If cnt == 1: one operation eliminates the single 1-bit
    • If cnt > 1: one operation (addition) converts multiple consecutive 1-bits into a single 1-bit at a higher position

After processing all bits, any remaining consecutive 1-bits are handled:

  • If cnt == 1: one more operation needed
  • If cnt > 1: two more operations needed (one to convert to a higher bit, one to eliminate it)
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To understand why this approach works, let's think about what happens when we add or subtract powers of 2 in binary representation.

When we subtract a power of 2 from n, we're essentially flipping a single 1-bit to 0. For example, if n = 5 (101 in binary) and we subtract 4 (100), we get 1 (001). This is straightforward for isolated 1-bits.

However, the challenge comes with consecutive 1-bits. Consider n = 7 (111 in binary). We could subtract powers of 2 three times: 7 - 4 = 3, then 3 - 2 = 1, then 1 - 1 = 0. That's 3 operations.

But here's the key insight: instead of subtracting, we can add! If we add 1 to 7, we get 8 (1000 in binary). Notice how adding 1 to 111 creates 1000 - all those consecutive 1-bits "collapse" into a single 1-bit at a higher position due to the carry propagation. Now we only need one more operation to subtract 8, giving us a total of 2 operations instead of 3.

This pattern generalizes: whenever we see consecutive 1-bits in the binary representation, it's more efficient to add a power of 2 to create a carry that eliminates all but one of them, rather than subtracting each one individually.

The algorithm essentially simulates this process by scanning through the binary representation:

  • Single 1-bits can be directly eliminated (1 operation)
  • Multiple consecutive 1-bits should be "collapsed" into a single higher bit through addition (1 operation), which then needs to be eliminated later (another operation)

This greedy strategy of always choosing to add when facing consecutive 1-bits and subtract for isolated 1-bits gives us the minimum number of operations.

Learn more about Greedy and Dynamic Programming patterns.

Solution Approach

The solution implements a greedy algorithm that processes the binary representation of n bit by bit from right to left using bitwise operations.

Algorithm Steps:

  1. Initialize variables:

    • ans: counts the total number of operations needed
    • cnt: tracks the current number of consecutive 1-bits encountered
  2. Process each bit from right to left:

    while n:
        if n & 1:  # Current bit is 1
            cnt += 1
        elif cnt:  # Current bit is 0 and we have accumulated 1-bits
            ans += 1
            cnt = 0 if cnt == 1 else 1
        n >>= 1  # Shift right to process next bit
    • When we encounter a 1-bit (n & 1 is true), we increment cnt to count consecutive 1s
    • When we encounter a 0-bit after some 1-bits:
      • If cnt == 1: We had a single isolated 1-bit, which takes 1 operation to eliminate. Reset cnt to 0
      • If cnt > 1: We had consecutive 1-bits. One operation (addition) converts them to a single 1-bit at a higher position. Set cnt = 1 to account for this carried bit
  3. Handle remaining bits after the loop:

    if cnt == 1:
        ans += 1
    elif cnt > 1:
        ans += 2
    • If cnt == 1: One more operation to eliminate the final 1-bit
    • If cnt > 1: Two operations needed - one to convert consecutive 1s to a higher bit, another to eliminate that bit

Example Walkthrough:

For n = 39 (binary: 100111):

  • Process bits right to left: 1, 1, 1, 0, 0, 1
  • First three 1s: cnt becomes 3
  • First 0: Since cnt = 3 > 1, we add 1 operation and set cnt = 1 (carried bit)
  • Second 0: Since cnt = 1, we add 1 operation and set cnt = 0
  • Final 1: cnt becomes 1
  • After loop: cnt = 1, so add 1 more operation
  • Total: 3 operations

The algorithm efficiently handles the pattern where consecutive 1-bits should be "collapsed" through addition rather than eliminated one by one through subtraction.

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 trace through n = 14 (binary: 1110) to illustrate the solution approach:

Initial state: n = 14 (binary: 1110), ans = 0, cnt = 0

Step 1: Process rightmost bit (0)

  • n & 1 = 0, so current bit is 0
  • cnt = 0, so we skip the elif block
  • Shift right: n = 7 (binary: 111)

Step 2: Process next bit (1)

  • n & 1 = 1, so current bit is 1
  • Increment cnt = 1
  • Shift right: n = 3 (binary: 11)

Step 3: Process next bit (1)

  • n & 1 = 1, so current bit is 1
  • Increment cnt = 2
  • Shift right: n = 1 (binary: 1)

Step 4: Process next bit (1)

  • n & 1 = 1, so current bit is 1
  • Increment cnt = 3
  • Shift right: n = 0

Step 5: Loop ends (n = 0)

  • We have cnt = 3 (three consecutive 1-bits)
  • Since cnt > 1, we need 2 operations:
    • First operation: Add 2 to convert 1110 to 10000 (collapsing the three 1s into a single 1)
    • Second operation: Subtract 16 to reach 0
  • ans += 2

Final answer: 2 operations

This demonstrates how consecutive 1-bits (111 in positions 1-3) are more efficiently handled by adding a power of 2 to create a carry, rather than subtracting each bit individually (which would take 3 operations).

Solution Implementation

1class Solution:
2    def minOperations(self, n: int) -> int:
3        # Initialize result counter and consecutive 1-bits counter
4        operations = 0
5        consecutive_ones = 0
6      
7        # Process each bit of n from right to left
8        while n > 0:
9            # Check if the current least significant bit is 1
10            if n & 1:
11                # Increment counter for consecutive 1-bits
12                consecutive_ones += 1
13            elif consecutive_ones > 0:
14                # We hit a 0 after seeing 1s, process the group of 1s
15                operations += 1
16                # Reset counter: if we had exactly one 1, start fresh
17                # Otherwise, carry over 1 (for cases like 11 -> 100)
18                consecutive_ones = 0 if consecutive_ones == 1 else 1
19          
20            # Right shift to check the next bit
21            n >>= 1
22      
23        # Handle any remaining consecutive 1-bits after the loop
24        if consecutive_ones == 1:
25            # Single 1-bit requires one operation
26            operations += 1
27        elif consecutive_ones > 1:
28            # Multiple consecutive 1-bits require two operations
29            operations += 2
30      
31        return operations
32
1class Solution {
2    public int minOperations(int n) {
3        int operationCount = 0;
4        int consecutiveOnes = 0;
5      
6        // Process each bit of n from right to left
7        while (n > 0) {
8            // Check if the current least significant bit is 1
9            if ((n & 1) == 1) {
10                // Increment count of consecutive 1-bits
11                consecutiveOnes++;
12            } else if (consecutiveOnes > 0) {
13                // We've encountered a 0 after a sequence of 1-bits
14                // Process the group of consecutive 1-bits
15                operationCount++;
16              
17                // If we had exactly one 1-bit, we're done with this group
18                // If we had multiple 1-bits, we carry over 1 (like binary addition)
19                consecutiveOnes = (consecutiveOnes == 1) ? 0 : 1;
20            }
21          
22            // Shift n right by 1 bit to process the next bit
23            n >>= 1;
24        }
25      
26        // Handle any remaining consecutive 1-bits at the most significant positions
27        if (consecutiveOnes == 1) {
28            // Single remaining 1-bit requires one operation
29            operationCount += 1;
30        } else if (consecutiveOnes > 1) {
31            // Multiple remaining 1-bits require two operations
32            operationCount += 2;
33        }
34      
35        return operationCount;
36    }
37}
38
1class Solution {
2public:
3    int minOperations(int n) {
4        int operations = 0;           // Total number of operations needed
5        int consecutiveOnes = 0;       // Count of consecutive 1-bits encountered
6      
7        // Process each bit of n from right to left
8        while (n > 0) {
9            if ((n & 1) == 1) {
10                // Current bit is 1: increment consecutive ones counter
11                consecutiveOnes++;
12            } else if (consecutiveOnes > 0) {
13                // Current bit is 0 and we have accumulated consecutive 1s
14                // Time to process the group of consecutive 1s we just finished counting
15                operations++;
16              
17                // If we had exactly one 1-bit, we can clear it with one operation
18                // If we had multiple consecutive 1s, we need to handle carry-over
19                consecutiveOnes = (consecutiveOnes == 1) ? 0 : 1;
20            }
21          
22            n >>= 1;  // Shift to examine the next bit
23        }
24      
25        // Handle any remaining consecutive 1s after processing all bits
26        if (consecutiveOnes == 1) {
27            operations += 1;  // Single 1-bit requires one operation
28        } else if (consecutiveOnes > 1) {
29            operations += 2;  // Multiple consecutive 1s require two operations
30        }
31      
32        return operations;
33    }
34};
35
1function minOperations(n: number): number {
2    let operationCount: number = 0;
3    let consecutiveOnes: number = 0;
4  
5    // Process each bit of n from right to left
6    while (n > 0) {
7        // Check if the current least significant bit is 1
8        if (n & 1) {
9            // Count consecutive 1-bits
10            consecutiveOnes++;
11        } else if (consecutiveOnes > 0) {
12            // When we encounter a 0 after consecutive 1s, process them
13            operationCount++;
14          
15            // If we had exactly one 1-bit, reset counter
16            // If we had multiple consecutive 1-bits, keep one (for carry effect)
17            consecutiveOnes = consecutiveOnes === 1 ? 0 : 1;
18        }
19      
20        // Shift n right by 1 bit to process the next bit
21        n >>= 1;
22    }
23  
24    // Handle remaining consecutive 1-bits after processing all bits
25    if (consecutiveOnes === 1) {
26        // Single remaining 1-bit requires one operation
27        operationCount++;
28    } else if (consecutiveOnes > 1) {
29        // Multiple consecutive 1-bits at the end require two operations
30        operationCount += 2;
31    }
32  
33    return operationCount;
34}
35

Time and Space Complexity

The time complexity is O(log n), where n is the given integer. This is because the while loop iterates through each bit of the binary representation of n. In each iteration, we perform a right shift operation (n >>= 1), which effectively divides n by 2. The number of iterations required to reduce n to 0 through repeated division by 2 is log₂(n), hence the logarithmic time complexity.

The space complexity is O(1) as the algorithm only uses a constant amount of extra space. The variables ans and cnt are the only additional storage used, and their space requirements do not depend on the size of the input n.

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

Common Pitfalls

1. Forgetting to Handle Remaining Bits After the Loop

The Pitfall: A common mistake is only processing bits while iterating through n and forgetting that there might be consecutive 1-bits remaining after the loop ends. This happens when the most significant bits are 1s.

Incorrect Implementation:

def minOperations(self, n: int) -> int:
    operations = 0
    consecutive_ones = 0
  
    while n > 0:
        if n & 1:
            consecutive_ones += 1
        elif consecutive_ones > 0:
            operations += 1
            consecutive_ones = 0 if consecutive_ones == 1 else 1
        n >>= 1
  
    # Missing: handling of remaining consecutive_ones!
    return operations

Why It Fails: For n = 4 (binary: 100), the loop processes the bit and consecutive_ones becomes 1, but we never add the operation needed to eliminate this final 1-bit. The function would return 0 instead of 1.

The Fix: Always check and handle any remaining consecutive 1-bits after the main loop:

if consecutive_ones == 1:
    operations += 1
elif consecutive_ones > 1:
    operations += 2

2. Incorrect Carry-Over Logic for Consecutive 1-bits

The Pitfall: When encountering a 0-bit after multiple consecutive 1-bits, incorrectly resetting consecutive_ones to 0 instead of 1. The key insight is that adding a power of 2 to consecutive 1-bits creates a carry that propagates to the next higher bit position.

Incorrect Implementation:

elif consecutive_ones > 0:
    operations += 1
    consecutive_ones = 0  # Wrong! Should be 1 when consecutive_ones > 1

Why It Fails: For n = 3 (binary: 11), after processing the two 1-bits and encountering the implicit 0 at the end, we should recognize that adding 1 converts 11 to 100, leaving us with one more 1-bit to handle. If we reset to 0, we miss accounting for this carried bit.

The Fix: Use conditional logic to properly handle the carry:

consecutive_ones = 0 if consecutive_ones == 1 else 1

3. Misunderstanding When to Increment Operations

The Pitfall: Incrementing the operation counter immediately when seeing 1-bits, rather than waiting to process a complete group of consecutive 1s.

Incorrect Implementation:

while n > 0:
    if n & 1:
        consecutive_ones += 1
        operations += 1  # Wrong! Don't count operations here
    # ...

Why It Fails: This approach counts each 1-bit as an operation, missing the optimization that consecutive 1-bits can be handled more efficiently through addition rather than multiple subtractions.

The Fix: Only increment operations when you've identified a complete group of consecutive 1-bits (when transitioning from 1s to 0, or at the end of processing):

if n & 1:
    consecutive_ones += 1  # Just count, don't add operations yet
elif consecutive_ones > 0:
    operations += 1  # Now we know the group size, add operation
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of these pictures shows the visit order of a depth-first search?


Recommended Readings

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

Load More