Facebook Pixel

2169. Count Operations to Obtain Zero

EasyMathSimulation
Leetcode Link

Problem Description

You are given two non-negative integers num1 and num2.

The task is to perform a series of operations until one of the numbers becomes 0. In each operation:

  • If num1 >= num2, subtract num2 from num1
  • Otherwise, subtract num1 from num2

For example:

  • Starting with num1 = 5 and num2 = 4:
    • Since 5 >= 4, we perform num1 = 5 - 4 = 1, resulting in num1 = 1 and num2 = 4
  • Starting with num1 = 4 and num2 = 5:
    • Since 4 < 5, we perform num2 = 5 - 4 = 1, resulting in num1 = 4 and num2 = 1

The goal is to return the total number of operations required to make either num1 = 0 or num2 = 0.

This process simulates repeatedly subtracting the smaller number from the larger number until one of them reaches zero, counting each subtraction as one operation.

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

Intuition

The problem is essentially asking us to simulate a process that resembles finding the greatest common divisor (GCD) using the subtraction method, but we need to count the steps rather than find the GCD itself.

The key observation is that we're always subtracting the smaller number from the larger one. This process continues until one number becomes 0. At each step, we're reducing the total sum of the two numbers, guaranteeing that the process will eventually terminate.

Let's trace through an example to understand the pattern:

  • If we have num1 = 7 and num2 = 3:
    • Step 1: 7 - 3 = 4, so num1 = 4, num2 = 3
    • Step 2: 4 - 3 = 1, so num1 = 1, num2 = 3
    • Step 3: 3 - 1 = 2, so num1 = 1, num2 = 2
    • Step 4: 2 - 1 = 1, so num1 = 1, num2 = 1
    • Step 5: 1 - 1 = 0, so num1 = 0, num2 = 1
    • Total operations: 5

The straightforward approach is to directly simulate what the problem describes: keep track of both numbers, perform the subtraction based on which is larger, and count each operation. Since we're always making progress (reducing at least one number), and the numbers are non-negative, we're guaranteed to reach 0 eventually.

The simulation approach works well here because:

  1. The operation rules are clearly defined
  2. We have a clear termination condition (when either number becomes 0)
  3. Each operation brings us closer to the termination condition

Learn more about Math patterns.

Solution Approach

The solution implements a direct simulation approach using a simple while loop. Here's how the implementation works:

  1. Initialize a counter: We start with ans = 0 to keep track of the number of operations performed.

  2. Loop condition: We use while num1 and num2 which continues as long as both numbers are non-zero. The loop terminates when either num1 or num2 becomes 0.

  3. Perform the operation: Inside the loop, we check which number is larger:

    • If num1 >= num2, we subtract num2 from num1: num1 = num1 - num2
    • Otherwise, we subtract num1 from num2: num2 = num2 - num1
  4. Count the operation: After each subtraction, we increment the counter: ans += 1

  5. Return the result: Once the loop exits (when one number becomes 0), we return the total count ans.

The algorithm follows these steps:

ans = 0
while num1 and num2:
    if num1 >= num2:
        num1 -= num2
    else:
        num2 -= num1
    ans += 1
return ans

Time Complexity: The time complexity depends on the values of num1 and num2. In the worst case, if one number is much larger than the other, we might need to perform many subtractions. The complexity is proportional to the larger number divided by the smaller number.

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

The beauty of this solution lies in its simplicity - it directly translates the problem statement into code without any complex transformations or optimizations.

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 num1 = 10 and num2 = 3:

Initial state: num1 = 10, num2 = 3, ans = 0

Operation 1: Since 10 >= 3, we subtract: num1 = 10 - 3 = 7

  • Result: num1 = 7, num2 = 3, ans = 1

Operation 2: Since 7 >= 3, we subtract: num1 = 7 - 3 = 4

  • Result: num1 = 4, num2 = 3, ans = 2

Operation 3: Since 4 >= 3, we subtract: num1 = 4 - 3 = 1

  • Result: num1 = 1, num2 = 3, ans = 3

Operation 4: Since 1 < 3, we subtract: num2 = 3 - 1 = 2

  • Result: num1 = 1, num2 = 2, ans = 4

Operation 5: Since 1 < 2, we subtract: num2 = 2 - 1 = 1

  • Result: num1 = 1, num2 = 1, ans = 5

Operation 6: Since 1 >= 1, we subtract: num1 = 1 - 1 = 0

  • Result: num1 = 0, num2 = 1, ans = 6

Termination: Since num1 = 0, the while loop exits.

Return: The function returns ans = 6

The process required 6 operations to reduce one of the numbers to 0. Notice how we always subtract the smaller value from the larger one, gradually reducing the numbers until one reaches zero.

Solution Implementation

1class Solution:
2    def countOperations(self, num1: int, num2: int) -> int:
3        """
4        Count the number of operations to reduce both numbers to zero.
5        In each operation, subtract the smaller number from the larger one.
6      
7        Args:
8            num1: First positive integer
9            num2: Second positive integer
10          
11        Returns:
12            Number of operations performed until one number becomes zero
13        """
14        # Initialize operation counter
15        operation_count = 0
16      
17        # Continue operations while both numbers are non-zero
18        while num1 != 0 and num2 != 0:
19            # Subtract the smaller number from the larger one
20            if num1 >= num2:
21                num1 -= num2
22            else:
23                num2 -= num1
24          
25            # Increment the operation counter
26            operation_count += 1
27      
28        return operation_count
29
1class Solution {
2    /**
3     * Counts the number of operations to reduce one number to zero
4     * by repeatedly subtracting the smaller from the larger.
5     * This implements a variant of the Euclidean algorithm.
6     * 
7     * @param num1 First non-negative integer
8     * @param num2 Second non-negative integer
9     * @return Number of operations performed until one number becomes zero
10     */
11    public int countOperations(int num1, int num2) {
12        // Initialize operation counter
13        int operationCount = 0;
14      
15        // Continue operations while both numbers are non-zero
16        while (num1 != 0 && num2 != 0) {
17            // Subtract the smaller number from the larger number
18            if (num1 >= num2) {
19                num1 -= num2;
20            } else {
21                num2 -= num1;
22            }
23          
24            // Increment the operation counter after each subtraction
25            operationCount++;
26        }
27      
28        // Return the total number of operations performed
29        return operationCount;
30    }
31}
32
1class Solution {
2public:
3    /**
4     * Counts the number of operations needed until one number becomes zero.
5     * In each operation, subtract the smaller number from the larger number.
6     * 
7     * @param num1 First positive integer
8     * @param num2 Second positive integer
9     * @return Number of operations performed
10     */
11    int countOperations(int num1, int num2) {
12        int operationCount = 0;
13      
14        // Continue operations while both numbers are non-zero
15        while (num1 != 0 && num2 != 0) {
16            // Subtract the smaller number from the larger number
17            if (num1 >= num2) {
18                num1 -= num2;
19            } else {
20                num2 -= num1;
21            }
22          
23            // Increment the operation counter
24            operationCount++;
25        }
26      
27        return operationCount;
28    }
29};
30
1/**
2 * Counts the number of operations needed to reduce one of the two numbers to zero
3 * by repeatedly subtracting the smaller number from the larger number
4 * @param num1 - The first positive integer
5 * @param num2 - The second positive integer
6 * @returns The total number of operations performed
7 */
8function countOperations(num1: number, num2: number): number {
9    // Initialize operation counter
10    let operationCount: number = 0;
11  
12    // Continue while both numbers are non-zero
13    while (num1 !== 0 && num2 !== 0) {
14        // Subtract the smaller number from the larger number
15        if (num1 >= num2) {
16            num1 -= num2;
17        } else {
18            num2 -= num1;
19        }
20      
21        // Increment the operation counter
22        operationCount++;
23    }
24  
25    // Return the total number of operations
26    return operationCount;
27}
28

Time and Space Complexity

The time complexity is O(max(num1, num2)) in the worst case. This occurs when one number is much larger than the other, or when the numbers are consecutive (like num1 = n and num2 = 1). In such cases, the algorithm essentially performs repeated subtractions similar to the Euclidean algorithm for finding GCD. For example, with num1 = 100 and num2 = 1, the loop will execute 100 times, subtracting 1 from 100 repeatedly until num1 becomes 0.

More precisely, the number of operations is bounded by max(num1, num2) / min(num1, num2) when min(num1, num2) > 0. The algorithm behaves similarly to the subtraction-based GCD algorithm, where each iteration reduces the larger number by the smaller one.

The space complexity is O(1) as the algorithm only uses a constant amount of extra space. It modifies the input parameters num1 and num2 in-place and only maintains one additional variable ans to count the operations. No additional data structures or recursive calls are used that would increase the space complexity.

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

Common Pitfalls

1. Inefficiency with Large Number Differences

The Pitfall: When one number is significantly larger than the other (e.g., num1 = 1000000 and num2 = 1), the naive simulation approach performs an excessive number of operations. Each iteration only subtracts the smaller value once, leading to potentially millions of iterations.

Example Scenario:

  • With num1 = 1000 and num2 = 3, the algorithm would perform 333 subtractions one by one
  • This becomes extremely slow for larger values like num1 = 10^9 and num2 = 1

Solution - Optimization Using Division: Instead of subtracting repeatedly, we can calculate how many times the smaller number fits into the larger number using integer division. This reduces multiple operations into a single calculation:

class Solution:
    def countOperations(self, num1: int, num2: int) -> int:
        operation_count = 0
      
        while num1 != 0 and num2 != 0:
            if num1 >= num2:
                # Calculate how many times num2 fits into num1
                operations_batch = num1 // num2
                operation_count += operations_batch
                # Update num1 to the remainder
                num1 = num1 % num2
            else:
                # Calculate how many times num1 fits into num2
                operations_batch = num2 // num1
                operation_count += operations_batch
                # Update num2 to the remainder
                num2 = num2 % num1
      
        return operation_count

This optimization transforms the time complexity from O(max(num1, num2)) to O(log(min(num1, num2))), which is essentially the Euclidean algorithm for finding GCD.

2. Not Handling Edge Cases

The Pitfall: Failing to consider when one or both inputs are already zero.

Solution: Add an early return check:

if num1 == 0 or num2 == 0:
    return 0

3. Integer Overflow in Other Languages

The Pitfall: While Python handles arbitrary precision integers automatically, implementing this in languages like C++ or Java might face integer overflow issues during intermediate calculations.

Solution: Use appropriate data types (like long long in C++ or long in Java) and be mindful of the maximum values that can be handled.

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 56?

1KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13    def dfs(path, res):
14        if len(path) == len(digits):
15            res.append(''.join(path))
16            return
17
18        next_number = digits[len(path)]
19        for letter in KEYBOARD[next_number]:
20            path.append(letter)
21            dfs(path, res)
22            path.pop()
23
24    res = []
25    dfs([], res)
26    return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2    '2', "abc".toCharArray(),
3    '3', "def".toCharArray(),
4    '4', "ghi".toCharArray(),
5    '5', "jkl".toCharArray(),
6    '6', "mno".toCharArray(),
7    '7', "pqrs".toCharArray(),
8    '8', "tuv".toCharArray(),
9    '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13    List<String> res = new ArrayList<>();
14    dfs(new StringBuilder(), res, digits.toCharArray());
15    return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19    if (path.length() == digits.length) {
20        res.add(path.toString());
21        return;
22    }
23    char next_digit = digits[path.length()];
24    for (char letter : KEYBOARD.get(next_digit)) {
25        path.append(letter);
26        dfs(path, res, digits);
27        path.deleteCharAt(path.length() - 1);
28    }
29}
30
1const KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13    let res = [];
14    dfs(digits, [], res);
15    return res;
16}
17
18function dfs(digits, path, res) {
19    if (path.length === digits.length) {
20        res.push(path.join(''));
21        return;
22    }
23    let next_number = digits.charAt(path.length);
24    for (let letter of KEYBOARD[next_number]) {
25        path.push(letter);
26        dfs(digits, path, res);
27        path.pop();
28    }
29}
30

Recommended Readings

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

Load More