Facebook Pixel

2154. Keep Multiplying Found Values by Two

EasyArrayHash TableSortingSimulation
Leetcode Link

Problem Description

You have an array of integers nums and a starting integer value called original. Your task is to perform a series of operations on original based on whether it exists in the array.

The process works as follows:

  1. Check if original exists in nums
  2. If it exists, multiply original by 2 (update original to 2 * original)
  3. If it doesn't exist, stop the process
  4. Repeat steps 1-3 with the new value of original

Keep repeating this process until you encounter a value that is not present in nums. Return the final value of original when the process stops.

For example, if nums = [5, 3, 6, 1, 12] and original = 3:

  • 3 is in nums, so original becomes 6
  • 6 is in nums, so original becomes 12
  • 12 is in nums, so original becomes 24
  • 24 is not in nums, so we stop and return 24

The solution uses a hash table (set) to store all numbers from nums for O(1) lookup time. It then continuously doubles original (using left shift <<= 1 which is equivalent to multiplying by 2) while it exists in the set, returning the final value when it's no longer found.

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

Intuition

The key insight is that we need to repeatedly check if a value exists in the array. Since we might need to check multiple values (original, 2×original, 4×original, etc.), doing a linear search through the array each time would be inefficient.

This naturally leads us to think about preprocessing the array into a data structure that supports fast lookups. A hash table (or set in this case) is perfect because:

  1. We can check if a value exists in O(1) time
  2. We don't care about the order or frequency of elements, just their presence
  3. The one-time cost of O(n) to build the set is worth it since we might need multiple lookups

Once we have fast lookups, the algorithm becomes straightforward - we just keep checking and doubling. The doubling operation can be done with multiplication original * 2 or with a left bit shift original << 1 (shifting bits left by 1 position doubles the number in binary).

The process must eventually terminate because we're doubling the value each time. Even if every possible doubled value existed in the array, we would eventually exceed the maximum possible integer value in the array, guaranteeing the loop stops.

Learn more about Sorting patterns.

Solution Approach

The implementation uses a hash table to optimize the lookup operations:

  1. Convert array to set: First, we create a set s from the input array nums. This conversion takes O(n) time but allows us to check membership in O(1) time for all subsequent operations.

    s = set(nums)
  2. Iterative doubling with lookup: We use a while loop to continuously check if original exists in the set. As long as it exists, we double the value:

    while original in s:
        original <<= 1

    The operation original <<= 1 is a left bit shift by 1 position, which effectively multiplies the number by 2. This is equivalent to original = original * 2 but is slightly more efficient at the bit level.

  3. Return the result: Once original is no longer in the set, the while loop terminates and we return the current value of original.

Time Complexity: O(n + m) where n is the length of nums and m is the number of doubling operations performed. In the worst case, m could be log(max_value/original) where max_value is the largest number in the array.

Space Complexity: O(n) for storing the set containing all elements from nums.

The beauty of this solution lies in its simplicity - by preprocessing the array into a set, we transform what could be an O(n×m) algorithm (checking the array m times) into an O(n + m) algorithm.

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 a small example with nums = [2, 8, 4, 16] and original = 2:

Step 1: Create a set from the array

  • Convert nums to a set: s = {2, 4, 8, 16}
  • This allows O(1) lookup time for checking if values exist

Step 2: Iteratively check and double

Iteration 1:

  • Check: Is original = 2 in set s? YES (2 is in {2, 4, 8, 16})
  • Action: Double the value using left shift: original = 2 << 1 = 4

Iteration 2:

  • Check: Is original = 4 in set s? YES (4 is in {2, 4, 8, 16})
  • Action: Double the value: original = 4 << 1 = 8

Iteration 3:

  • Check: Is original = 8 in set s? YES (8 is in {2, 4, 8, 16})
  • Action: Double the value: original = 8 << 1 = 16

Iteration 4:

  • Check: Is original = 16 in set s? YES (16 is in {2, 4, 8, 16})
  • Action: Double the value: original = 16 << 1 = 32

Iteration 5:

  • Check: Is original = 32 in set s? NO (32 is not in {2, 4, 8, 16})
  • Action: Stop the loop

Step 3: Return the final value

  • Return original = 32

The process traced through the sequence: 2 → 4 → 8 → 16 → 32, stopping at 32 because it's not in the original array.

Solution Implementation

1class Solution:
2    def findFinalValue(self, nums: List[int], original: int) -> int:
3        # Convert the list to a set for O(1) lookup time
4        nums_set = set(nums)
5      
6        # Keep doubling the original value while it exists in the set
7        while original in nums_set:
8            # Left shift by 1 is equivalent to multiplying by 2
9            original = original << 1
10      
11        # Return the final value after all doublings
12        return original
13
1class Solution {
2  
3    /**
4     * Finds the final value after doubling the original value whenever it exists in the array.
5     * The process continues until the value is not found in the array.
6     * 
7     * @param nums     The input array of integers
8     * @param original The starting value to check and potentially double
9     * @return The final value after all doubling operations
10     */
11    public int findFinalValue(int[] nums, int original) {
12        // Create a HashSet to store all unique values from the array for O(1) lookup
13        Set<Integer> numSet = new HashSet<>();
14      
15        // Add all elements from the array to the set
16        for (int num : nums) {
17            numSet.add(num);
18        }
19      
20        // Keep doubling the original value while it exists in the set
21        while (numSet.contains(original)) {
22            // Double the original value using left shift operation (equivalent to multiplying by 2)
23            original <<= 1;
24        }
25      
26        // Return the final value that is not present in the set
27        return original;
28    }
29}
30
1class Solution {
2public:
3    int findFinalValue(vector<int>& nums, int original) {
4        // Create a hash set from the input array for O(1) lookup
5        unordered_set<int> numSet(nums.begin(), nums.end());
6      
7        // Keep doubling the original value while it exists in the set
8        while (numSet.count(original)) {
9            original *= 2;  // Double the value (equivalent to left shift by 1)
10        }
11      
12        // Return the final value that doesn't exist in the set
13        return original;
14    }
15};
16
1/**
2 * Finds the final value after repeatedly doubling the original value
3 * while it exists in the array
4 * @param nums - Array of numbers to check against
5 * @param original - Starting value to potentially double
6 * @returns The final value after all doubling operations
7 */
8function findFinalValue(nums: number[], original: number): number {
9    // Create a set from the input array for O(1) lookup time
10    const numSet: Set<number> = new Set(nums);
11  
12    // Keep doubling the original value while it exists in the set
13    while (numSet.has(original)) {
14        // Left shift by 1 is equivalent to multiplying by 2
15        original <<= 1;
16    }
17  
18    // Return the final value when it's no longer in the set
19    return original;
20}
21

Time and Space Complexity

The time complexity is O(n), and the space complexity is O(n). Here, n is the length of the array nums.

Time Complexity Analysis:

  • Converting the list nums to a set takes O(n) time
  • The while loop performs membership checks in the set, where each check takes O(1) time on average
  • The maximum number of iterations in the while loop is bounded by O(log(max_value)) where max_value is the maximum possible value that original can reach
  • Since the values are bounded by practical integer limits, the while loop iterations are effectively constant
  • Therefore, the overall time complexity is dominated by the set creation: O(n)

Space Complexity Analysis:

  • Creating a set from the array nums requires O(n) space to store all unique elements
  • The only other variable used is original, which takes O(1) space
  • Therefore, the total space complexity is O(n)

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

Common Pitfalls

1. Integer Overflow in Other Languages

While Python handles arbitrarily large integers automatically, in languages like Java or C++, continuously doubling values can lead to integer overflow. For instance, if you start with a large original value and it exists multiple times in doubled form, you might exceed the maximum integer limit.

Solution: In languages with fixed integer sizes, consider:

  • Using long/long long data types
  • Adding overflow checks before doubling
  • Setting a reasonable upper bound for the operation

2. Inefficient Array Lookup Instead of Set

A common mistake is checking membership directly in the array instead of converting it to a set first:

# Inefficient approach - DO NOT USE
while original in nums:  # O(n) lookup each time
    original <<= 1

This degrades performance from O(n + m) to O(n × m) where m is the number of doubling operations.

Solution: Always convert to a set first for O(1) lookups:

nums_set = set(nums)
while original in nums_set:  # O(1) lookup
    original <<= 1

3. Modifying the Original Array

Some might attempt to remove elements from nums as they're found, thinking it optimizes the search:

# Wrong approach - modifies input
while original in nums:
    nums.remove(original)  # O(n) operation and changes input
    original <<= 1

This not only has poor performance but also modifies the input array, which may not be allowed.

Solution: Use the immutable set approach without modifying the original input.

4. Bit Shift Operator Confusion

Using the wrong bit shift operator or shift amount:

# Common mistakes
original >> 1   # Right shift (divides by 2) instead of left shift
original << 2   # Shifts by 2 positions (multiplies by 4) instead of 2

Solution: Use original << 1 or original <<= 1 for doubling, or simply use original * 2 for clarity if bit operations are unfamiliar.

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

Which technique can we use to find the middle of a linked list?


Recommended Readings

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

Load More