89. Gray Code

MediumBit ManipulationMathBacktracking
Leetcode Link

Problem Description

An n-bit Gray code sequence is a special set of binary numbers. This sequence consists of 2^n integers that satisfy several conditions:

  • The integers range from 0 to 2^n - 1, inclusive, which means that all possible n-bit numbers are included.
  • The sequence starts with 0.
  • No integer is repeated; each number in the sequence is unique.
  • Consecutive numbers in the sequence differ by exactly one bit. This means if you look at the binary representation of any two adjacent numbers, they will be identical except for one bit.
  • Finally, the first and the last number in the sequence also differ by exactly one bit. This wraps the sequence around to form a circle, metaphorically speaking, where the end is just one step away from the beginning.

The problem asks us to construct such a sequence for a given n.

Intuition

The intuition behind the solution lies in recognising the pattern in which Gray code sequences are generated. A Gray code sequence can be constructed iteratively using a mathematical formula, which is simple yet powerful:

1G(i) = i ^ (i / 2).

To understand this, observe that for any binary number i, when you divide it by 2 (or equivalently shift it right by one bit: i >> 1), you are essentially moving all the bits to the right. XOR-ing (^) this shifted version with the original number i changes exactly one bit, thus satisfying the Gray code property. Also, since the sequence starts from 0, and we do this operation for all numbers from 0 to 2^n - 1, we ensure no duplicates while also encompassing the entire range.

The provided solution defines a Python function grayCode that takes an integer n as an input and returns a list of integers representing an n-bit Gray code sequence. The expression [i ^ (i >> 1) for i in range(1 << n)] generates the sequence. It uses a list comprehension where each element i from 0 to 2^n - 1 (generated by range(1 << n), since 1 << n is equal to 2^n) is transformed by the XOR operation with its right-shifted self, yielding the Gray code equivalent for that integer.

Learn more about Math and Backtracking patterns.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

Given an array of 1,000,000 integers that is almost sorted, except for 2 pairs of integers. Which algorithm is fastest for sorting the array?

Solution Approach

The solution approach leverages the mathematical properties of bitwise operations to efficiently generate the Gray code sequence. It applies the formula G(i) = i ^ (i >> 1) directly to ensure the desired properties of the Gray code:

  • Only one bit changes at a time.
  • The sequence is cyclic, and wraps back to the start after 2^n elements.

The code uses a for loop to iterate from 0 to 2^n - 1. For each number i within this range, the code computes the Gray code number using the formula. The key steps involving algorithms, data structures, or patterns are outlined below:

  1. Bit Shifting: By shifting i to the right by one bit (i >> 1), we effectively divide i by 2, achieving a new number whose binary representation is shifted one position.

  2. Bitwise XOR: The XOR operation (^) is then applied to the original number i and the right-shifted version of i. The property of XOR ensures that the resulting number will differ by exactly one bit from the original number i.

  3. List Comprehension: A list comprehension is used to elegantly apply the Gray code formula to each number in the range and collect the results into a list.

  4. << Operator: The << left shift operator is used to calculate 2^n as 1 << n, which serves as the upper bound in the range() function. This is an efficient way to compute powers of two.

The formula and loop together make the core of the algorithm, relying solely on the fundamental bitwise operations without the need for additional data structures. It's a direct application of the formula with no complex patterns or additional logic necessary, thus the code is both elegant and efficient.

Here is how the loop and Gray code generation works in practice:

1def grayCode(n: int) -> List[int]:
2    # Initializing the result list to store the Gray code sequence.
3    # Using list comprehension technique to generate the sequence.
4    # For each number i in the range from 0 to (2^n) - 1:
5    #   1. Shift the bits of i one position to the right using (i >> 1).
6    #   2. Apply XOR operation between i and (i >> 1) to get the Gray code equivalent.
7    #   3. Append the resulting Gray code number to the result list.
8    return [i ^ (i >> 1) for i in range(1 << n)]

By using the properties of bitwise operations, this approach generates a valid Gray code sequence with all the described characteristics in a single, succinct line of Python.

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

Which type of traversal does breadth first search do?

Example Walkthrough

Let's use a small example to illustrate the solution approach by walking through the construction of a 2-bit Gray code sequence.

Given n = 2, we aim to generate a sequence that contains 2^n = 2^2 = 4 integers. In binary, these numbers range from 00 to 11. Here's how we apply the solution:

  1. The list comprehension starts with i = 0. Computing G(0):

    • i = 0 (in binary 00)
    • i >> 1 = 0 (in binary 00)
    • G(0) = i ^ (i >> 1) = 00 ^ 00 = 00 in binary, which is 0 in decimal.
  2. Then for i = 1, computing G(1):

    • i = 1 (in binary 01)
    • i >> 1 = 0 (in binary 00)
    • G(1) = i ^ (i >> 1) = 01 ^ 00 = 01 in binary, which is 1 in decimal.
  3. Next for i = 2, computing G(2):

    • i = 2 (in binary 10)
    • i >> 1 = 1 (in binary 01)
    • G(2) = i ^ (i >> 1) = 10 ^ 01 = 11 in binary, which is 3 in decimal.
  4. Finally for i = 3, computing G(3):

    • i = 3 (in binary 11)
    • i >> 1 = 1 (in binary 01)
    • G(3) = i ^ (i >> 1) = 11 ^ 01 = 10 in binary, which is 2 in decimal.

The sequence, therefore, is [0, 1, 3, 2]. Let's verify the properties:

  • The integers range from 0 to 2^n - 1 (0 to 3 for n = 2), inclusive.
  • The sequence starts with 0.
  • There are no repeated integers.
  • Consecutive numbers differ by exactly one bit: Compare 00 with 01, then 01 with 11, and 11 with 10.
  • The sequence is cyclical: 0 (00) and 2 (10) also differ by one bit.

When n = 2, this function will generate the sequence [0, 1, 3, 2] efficiently by applying the method described in the solution approach. The resulting sequence adheres to all the rules of a Gray code sequence.

Solution Implementation

1from typing import List
2
3class Solution:
4    def grayCode(self, n: int) -> List[int]:
5        # Initialize an empty list to store the Gray code sequence
6        gray_code_sequence = []
7      
8        # Calculate 2^n, the total number of Gray code sequence numbers
9        total_numbers = 1 << n
10      
11        # Generate Gray code sequence
12        for i in range(total_numbers):
13            # Apply the formula: binary number XOR (binary number shifted right by one bit)
14            # The result is a number in the Gray code sequence
15            gray_number = i ^ (i >> 1)
16          
17            # Append the Gray code number to the sequence list
18            gray_code_sequence.append(gray_number)
19      
20        # Return the complete Gray code sequence
21        return gray_code_sequence
22
1class Solution {
2    public List<Integer> grayCode(int n) {
3        // Initialize an empty list to hold the Gray code sequence
4        List<Integer> result = new ArrayList<>();
5      
6        // Calculate the total number of elements in the Gray code sequence, which is 2^n
7        int totalNumbers = 1 << n;
8      
9        // Generate the Gray code sequence
10        for (int i = 0; i < totalNumbers; ++i) {
11            // The Gray code is generated by XOR-ing the current number 
12            // with the number right-shifted by one bit
13            int grayCodeNumber = i ^ (i >> 1);
14            // Add the generated Gray code number to the result list
15            result.add(grayCodeNumber);
16        }
17      
18        // Return the complete list of Gray code sequence
19        return result;
20    }
21}
22
1#include <vector> // Include the vector header for using the vector container.
2
3class Solution {
4public:
5    // Function to generate a Gray code sequence for a given number of bits 'n'.
6    std::vector<int> grayCode(int n) {
7        std::vector<int> result; // Create a vector to store the Gray code sequence.
8        int sequenceLength = 1 << n;  // 2^n, the total number of codes in the sequence.
9
10        // Generate the Gray code sequence using the binary to Gray code conversion formula
11        // which is G(i) = i ^ (i/2).
12        for (int i = 0; i < sequenceLength; ++i) {
13            result.push_back(i ^ (i >> 1)); // Apply the Gray code conversion and add it to the result vector.
14        }
15
16        // Return the complete Gray code sequence.
17        return result;
18    }
19};
20
1/**
2 * Generates the sequence of Gray codes for a given number of bits.
3 * Gray code is a binary numeral system where two successive values differ in only one bit.
4 * @param n The number of bits for the Gray code sequence.
5 * @return An array that represents the Gray code sequence.
6 */
7function grayCode(n: number): number[] {
8    // Initialize the array that will hold the Gray codes.
9    const grayCodes: number[] = [];
10  
11    // Calculate 2^n to determine the total number of codes in the sequence.
12    const totalCodes: number = 1 << n;
13  
14    // Generate the sequence of Gray codes.
15    for (let i = 0; i < totalCodes; ++i) {
16        // Convert the binary number to a Gray code by performing bitwise XOR
17        // of the number with itself right-shifted by one bit.
18        grayCodes.push(i ^ (i >> 1));
19    }
20  
21    // Return the computed Gray code sequence.
22    return grayCodes;
23}
24
25// Example usage:
26// const grayCodeSequence = grayCode(2);
27// console.log(grayCodeSequence); // Output: [0, 1, 3, 2]
28
Not Sure What to Study? Take the 2-min Quiz:

What is the worst case running time for finding an element in a binary tree (not necessarily binary search tree) of size n?

Time and Space Complexity

The time complexity of the given code is O(2^n) since it is generating all of the Gray codes for an n-bit number. Since the number of Gray codes is equal to 2^n, the loop in the list comprehension will iterate 2^n times.

The space complexity is also O(2^n) because the list that is being returned will contain 2^n elements, which corresponds to all the possible Gray codes for the n-bit number.

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

Fast Track Your Learning with Our Quick Skills Quiz:

Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?


Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫