Facebook Pixel

137. Single Number II

MediumBit ManipulationArray
Leetcode Link

Problem Description

You are given an integer array nums where every element appears three times except for one element, which appears exactly once. Your task is to find and return that single element.

The challenge has two important constraints:

  • Your solution must have linear runtime complexity (O(n))
  • Your solution must use only constant extra space (O(1))

For example, if nums = [2, 2, 3, 2], the answer would be 3 since it appears only once while 2 appears three times.

The key insight is to use bitwise operations. Since every number except one appears exactly three times, we can examine each bit position across all numbers. For any given bit position, if we count how many numbers have a 1 in that position, the count will either be divisible by 3 (if the single number has 0 in that position) or have a remainder of 1 when divided by 3 (if the single number has 1 in that position).

The solution iterates through all 32 bit positions (for a 32-bit integer), counts the number of 1s at each position across all numbers, and reconstructs the single number bit by bit. Special handling is needed for the sign bit (position 31) to correctly handle negative numbers.

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

Intuition

Let's think about what happens when numbers appear multiple times. If we had pairs (each number appearing twice), we could use XOR since a ^ a = 0. But with triplets, XOR doesn't directly help.

Consider looking at the problem from a different angle - what if we examine each bit position independently? Let's say we have [5, 5, 5, 3] in binary:

  • 5 = 101
  • 5 = 101
  • 5 = 101
  • 3 = 011

For bit position 0 (rightmost): we have three 1s from the 5s and one 1 from 3, totaling 4. Since 4 % 3 = 1, we know the single number has a 1 at position 0.

For bit position 1: we have zero 1s from the 5s and one 1 from 3, totaling 1. Since 1 % 3 = 1, the single number has a 1 at position 1.

For bit position 2: we have three 1s from the 5s and zero from 3, totaling 3. Since 3 % 3 = 0, the single number has a 0 at position 2.

This gives us 011 in binary, which is 3 - our answer!

The pattern emerges: for any bit position, if the total count of 1s is divisible by 3, then all the 1s come from the numbers that appear three times, meaning our single number has 0 at that position. If there's a remainder of 1 when dividing by 3, our single number contributes that extra 1.

By processing all 32 bit positions this way, we can reconstruct the single number bit by bit, using only constant space to store our result and a counter for each bit position.

Solution Approach

The implementation uses bitwise operations to examine each of the 32 bit positions individually:

  1. Initialize the result: Start with ans = 0 to build our answer bit by bit.

  2. Iterate through each bit position: Loop through positions 0 to 31 (covering all bits in a 32-bit integer).

  3. Count bits at each position: For bit position i, count how many numbers have a 1 at that position:

    • num >> i shifts the number right by i positions
    • & 1 isolates the bit at position 0 after shifting
    • sum(num >> i & 1 for num in nums) counts total 1s at position i
  4. Determine the bit value: If cnt % 3 is non-zero (has remainder 1), the single number has a 1 at position i:

    • For positions 0-30: Use ans |= 1 << i to set bit i to 1
    • For position 31 (sign bit): Use ans -= 1 << 31 to handle negative numbers correctly
  5. Handle negative numbers: The special case for bit 31 is crucial. If the single number is negative, its 31st bit is 1. In Python's two's complement representation, setting the sign bit requires subtracting 1 << 31 rather than using OR operation.

The algorithm processes each number exactly once for each bit position, giving us O(32n) = O(n) time complexity. We only use a few variables (ans, i, cnt), achieving O(1) space complexity.

For example, with nums = [2, 2, 3, 2]:

  • Bit 0: count = 4, 4 % 3 = 1, so bit 0 is 1
  • Bit 1: count = 3, 3 % 3 = 0, so bit 1 is 1
  • Result: 11 in binary = 3 in decimal

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 algorithm with nums = [5, 5, 5, 3]:

Step 1: Convert to binary for visualization

  • 5 = 00000101 (showing 8 bits for simplicity)
  • 3 = 00000011

Step 2: Process each bit position

Starting with ans = 0:

Bit position 0 (rightmost):

  • Check each number: 5 & 1 = 1, 5 & 1 = 1, 5 & 1 = 1, 3 & 1 = 1
  • Count = 4 ones
  • 4 % 3 = 1 (remainder exists)
  • Set bit 0 of ans: ans = 0 | 1 = 1

Bit position 1:

  • Check each number: (5 >> 1) & 1 = 0, (5 >> 1) & 1 = 0, (5 >> 1) & 1 = 0, (3 >> 1) & 1 = 1
  • Count = 1 one
  • 1 % 3 = 1 (remainder exists)
  • Set bit 1 of ans: ans = 1 | (1 << 1) = 1 | 2 = 3

Bit position 2:

  • Check each number: (5 >> 2) & 1 = 1, (5 >> 2) & 1 = 1, (5 >> 2) & 1 = 1, (3 >> 2) & 1 = 0
  • Count = 3 ones
  • 3 % 3 = 0 (no remainder)
  • Don't set bit 2, ans remains 3

Bit positions 3-31:

  • All have count = 0
  • 0 % 3 = 0 (no remainder)
  • No bits set, ans remains 3

Final result: ans = 3

The algorithm correctly identifies that the single number is 3 by reconstructing it bit by bit based on which bit positions have counts not divisible by 3.

Solution Implementation

1class Solution:
2    def singleNumber(self, nums: List[int]) -> int:
3        """
4        Find the single number that appears once while all others appear three times.
5      
6        Strategy: Count the number of 1s at each bit position across all numbers.
7        If count % 3 != 0, then the single number has a 1 at that position.
8      
9        Args:
10            nums: List of integers where every element appears three times except one
11          
12        Returns:
13            The single number that appears only once
14        """
15        result = 0
16      
17        # Process each bit position (0 to 31 for 32-bit integers)
18        for bit_position in range(32):
19            # Count how many numbers have a 1 at this bit position
20            bit_count = sum((num >> bit_position) & 1 for num in nums)
21          
22            # If count is not divisible by 3, the single number has a 1 here
23            if bit_count % 3:
24                # Handle sign bit (31st bit) specially for negative numbers
25                if bit_position == 31:
26                    # For Python's two's complement representation
27                    result -= (1 << bit_position)
28                else:
29                    # Set the bit at this position in the result
30                    result |= (1 << bit_position)
31                  
32        return result
33
1class Solution {
2    public int singleNumber(int[] nums) {
3        int result = 0;
4      
5        // Process each bit position (0 to 31) for 32-bit integers
6        for (int bitPosition = 0; bitPosition < 32; bitPosition++) {
7            int bitCount = 0;
8          
9            // Count how many numbers have a 1 at the current bit position
10            for (int number : nums) {
11                // Extract the bit at the current position and add to count
12                bitCount += (number >> bitPosition) & 1;
13            }
14          
15            // If bit count is not divisible by 3, the single number has a 1 at this position
16            // (All numbers appearing 3 times will contribute 0 or 3 to the count)
17            bitCount %= 3;
18          
19            // Set the bit at the current position in the result if count is 1
20            result |= (bitCount << bitPosition);
21        }
22      
23        return result;
24    }
25}
26
1class Solution {
2public:
3    int singleNumber(vector<int>& nums) {
4        int result = 0;
5      
6        // Process each bit position (0 to 31 for 32-bit integers)
7        for (int bitPosition = 0; bitPosition < 32; ++bitPosition) {
8            int bitCount = 0;
9          
10            // Count how many numbers have 1 at current bit position
11            for (int number : nums) {
12                // Extract the bit at current position and add to count
13                bitCount += ((number >> bitPosition) & 1);
14            }
15          
16            // If a number appears 3 times, its bits appear 3 times
17            // The single number's bit will make count = 1 or 4, 7, etc. (3k+1)
18            // Taking modulo 3 gives us the bit value of the single number
19            bitCount %= 3;
20          
21            // Set the bit at current position in result if count is 1
22            result |= (bitCount << bitPosition);
23        }
24      
25        return result;
26    }
27};
28
1/**
2 * Finds the single number that appears once while all other numbers appear three times
3 * Uses bit manipulation to count occurrences of each bit position across all numbers
4 * 
5 * @param nums - Array of integers where every element appears three times except for one
6 * @returns The single number that appears only once
7 */
8function singleNumber(nums: number[]): number {
9    let result = 0;
10  
11    // Process each bit position (0 to 31 for 32-bit integers)
12    for (let bitPosition = 0; bitPosition < 32; bitPosition++) {
13        // Count how many numbers have 1 at the current bit position
14        const bitCount = nums.reduce((accumulator, currentNumber) => {
15            // Extract the bit at current position using right shift and AND with 1
16            const bitValue = (currentNumber >> bitPosition) & 1;
17            return accumulator + bitValue;
18        }, 0);
19      
20        // If count % 3 is 1, the single number has 1 at this bit position
21        // Set the corresponding bit in result using OR and left shift
22        result |= (bitCount % 3) << bitPosition;
23    }
24  
25    return result;
26}
27

Time and Space Complexity

The time complexity is O(n × 32) which simplifies to O(n), where n is the length of the array. The code iterates through 32 bits (fixed constant for integer representation), and for each bit position, it performs a sum operation that traverses all n elements in the array. Since 32 is a constant, the overall time complexity is O(n).

The reference answer mentions O(n × log M) where M is the range of elements. This is equivalent because log M represents the number of bits needed to represent the maximum value in the array. For 32-bit integers, this is at most 32, so log M ≤ 32.

The space complexity is O(1). The algorithm only uses a fixed amount of extra space for variables ans, i, and cnt, regardless of the input size. No additional data structures that grow with input size are created.

Common Pitfalls

1. Incorrect Handling of Negative Numbers

The most common pitfall in this problem is failing to properly handle negative numbers when reconstructing the result bit by bit. Many developers mistakenly treat bit 31 (the sign bit) the same as other bits.

Incorrect approach:

# Wrong: Treating bit 31 like any other bit
if bit_count % 3:
    result |= (1 << bit_position)  # This doesn't work correctly for bit 31

Why it fails: In Python's integer representation, directly ORing with 1 << 31 doesn't produce the correct negative number due to Python's arbitrary precision integers. The value 1 << 31 equals 2147483648, and ORing this creates a large positive number instead of a negative one.

Correct approach:

if bit_count % 3:
    if bit_position == 31:
        result -= (1 << 31)  # Subtract to create negative number
    else:
        result |= (1 << bit_position)

2. Alternative Solution Using State Machine

Another approach uses a state machine with two variables to track bit counts modulo 3:

def singleNumber(self, nums: List[int]) -> int:
    ones = 0   # Bits that have appeared once (mod 3)
    twos = 0   # Bits that have appeared twice (mod 3)
  
    for num in nums:
        # Update twos: add bits from ones that are also in num
        twos |= ones & num
      
        # Update ones: XOR with num to toggle bits
        ones ^= num
      
        # Clear bits that have appeared three times
        # (bits that are in both ones and twos)
        threes = ones & twos
        ones &= ~threes
        twos &= ~threes
  
    return ones

This approach avoids the sign bit issue entirely by using bitwise operations that naturally preserve sign information, making it more elegant and less error-prone for handling negative numbers.

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