Facebook Pixel

1012. Numbers With Repeated Digits

Problem Description

Given an integer n, you need to count how many positive integers from 1 to n contain at least one repeated digit.

For example:

  • The number 11 has a repeated digit (the digit 1 appears twice)
  • The number 121 has a repeated digit (the digit 1 appears twice)
  • The number 123 has no repeated digits (all digits are unique)

The solution uses a digit dynamic programming approach with state compression. Instead of directly counting numbers with repeated digits, it calculates numbers with no repeated digits and subtracts from the total.

The key idea is to use a bitmask to track which digits (0-9) have already appeared in the number being formed. The dfs function explores all valid digit combinations while:

  • Tracking used digits via the mask parameter (bit j is set if digit j has been used)
  • Handling leading zeros with the lead parameter
  • Respecting the upper bound n with the limit parameter

For each position, the algorithm tries placing each valid digit (0-9), skipping those already used (checked via mask >> j & 1). The final answer is n - count_of_numbers_with_unique_digits.

The time complexity is O(log n × 2^10 × 10) where log n represents the number of digits, 2^10 represents possible digit masks, and 10 represents digit choices.

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

Intuition

When faced with counting numbers with "at least one" repeated digit, a direct approach becomes complicated - we'd need to track various cases like exactly one pair of repeated digits, two pairs, three of the same digit, etc. This quickly becomes unwieldy.

The key insight is to use complementary counting: instead of counting what we want directly, count what we don't want and subtract. Numbers with "at least one repeated digit" is the complement of numbers with "all unique digits". So the answer becomes n - count_of_unique_digit_numbers.

Counting numbers with all unique digits is much cleaner - we just need to ensure each digit we place hasn't been used before. This naturally leads to using a bitmask to track which of the 10 digits (0-9) have been used. If digit j has been used, we set bit j in our mask.

Why digit DP? When building numbers digit by digit from left to right, we need to:

  1. Respect the upper bound n (we can't exceed it)
  2. Handle leading zeros properly (they don't count as "used" digits)
  3. Track our state efficiently across recursive calls

The recursive structure emerges naturally: at each position, try all valid digits (0-9), mark them as used in the mask, and continue building the rest of the number. The limit flag ensures we don't exceed n - if we're at the limit and place the maximum allowed digit, the next position remains limited. The lead flag handles the special case where we haven't placed any non-zero digit yet.

The memoization with @cache is crucial because many different paths lead to the same state (same position, same used digits, same flags), allowing us to avoid redundant calculations.

Learn more about Math and Dynamic Programming patterns.

Solution Approach

The solution implements digit dynamic programming with state compression. Let's break down the implementation step by step:

1. State Definition

We define a recursive function dfs(i, mask, lead, limit) with four parameters:

  • i: Current digit position (0-indexed from left)
  • mask: Binary representation of used digits (bit j is 1 if digit j has been used)
  • lead: Boolean indicating if we're still in leading zeros
  • limit: Boolean indicating if we're bounded by the original number n

2. Base Case

if i >= len(s):
    return lead ^ 1

When we've processed all digits, we return 1 if we've formed a valid number (not just leading zeros), otherwise 0. The expression lead ^ 1 returns 0 if lead is True (all zeros), and 1 if lead is False (valid number).

3. Calculating Upper Bound

up = int(s[i]) if limit else 9

If we're limited by the original number, we can only place digits up to s[i]. Otherwise, we can use any digit from 0 to 9.

4. Digit Enumeration

For each position, we try all valid digits from 0 to up:

for j in range(up + 1):
    if lead and j == 0:
        ans += dfs(i + 1, mask, True, False)
    elif mask >> j & 1 ^ 1:
        ans += dfs(i + 1, mask | 1 << j, False, limit and j == up)
  • Leading zeros case: If we're still in leading zeros (lead = True) and place another 0, we continue with leading zeros without marking 0 as used. The limit becomes False since leading zeros are always less than any positive number.

  • Valid digit case: The condition mask >> j & 1 ^ 1 checks if digit j hasn't been used yet (equivalent to checking if bit j in mask is 0). If the digit is available:

    • We set bit j in the mask: mask | 1 << j
    • We mark lead as False since we've placed a non-zero digit
    • We update limit: it remains True only if we were limited AND placed the maximum digit

5. Memoization

The @cache decorator automatically memoizes the results, avoiding recalculation for identical states.

6. Final Calculation

s = str(n)
return n - dfs(0, 0, True, True)

We convert n to string for easy digit access, then subtract the count of numbers with unique digits from n to get numbers with at least one repeated digit.

The initial call uses:

  • i = 0: Start from the leftmost digit
  • mask = 0: No digits used yet
  • lead = True: Initially in leading zeros
  • limit = True: Initially bounded by n

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 the algorithm with n = 25 to understand how it counts numbers with unique digits.

Goal: Count numbers from 1 to 25 with at least one repeated digit. Strategy: Count numbers with ALL unique digits, then compute 25 - count.

Let's trace a few paths through the DFS to see how it builds valid numbers:

Initial Call: dfs(0, 0, True, True) with string "25"

  • Position 0, no digits used (mask=0000000000), in leading zeros, limited by "25"

Building "12":

  1. At position 0: Can place 0, 1, or 2 (limited by '2')

    • Choose 1: dfs(1, 0000000010, False, False)
    • Mask becomes 0000000010 (bit 1 set)
    • No longer in leading zeros, no longer limited (1 < 2)
  2. At position 1: Can place 0-9 except 1 (already used)

    • Choose 2: dfs(2, 0000000110, False, False)
    • Mask becomes 0000000110 (bits 1 and 2 set)
  3. At position 2: Reached end, return 1 (valid number formed)

Building "22" (will be rejected):

  1. At position 0: Choose 2: dfs(1, 0000000100, False, True)

    • Still limited because we placed the maximum digit '2'
  2. At position 1: Can only place 0-2 (limited by '2' in "25")

    • Try to place 2: Check mask >> 2 & 1 = 1 (digit 2 already used)
    • This path is blocked! Cannot reuse digit 2

Building "7" (single digit):

  1. At position 0: Choose 0 (leading zero): dfs(1, 0, True, False)

    • Remains in leading zeros, mask unchanged
  2. At position 1: Choose 7: dfs(2, 0010000000, False, False)

    • Exits leading zeros, marks digit 7 as used
  3. At position 2: Reached end, return 1

Complete Count: The algorithm systematically explores all paths, counting valid numbers:

  • Single digit numbers with unique digits: 1,2,3,4,5,6,7,8,9 (9 numbers)
  • Two digit numbers 10-25 with unique digits: 10,12,13,14,15,16,17,18,19,20,21,23,24,25 (14 numbers)
  • Total with unique digits: 23

Final Answer: 25 - 23 = 2

The two numbers with repeated digits are: 11 and 22

Let's work through n = 25 to see how the algorithm counts numbers with at least one repeated digit.

Setup: Convert 25 to string "25" and call dfs(0, 0, True, True)

The algorithm will count numbers from 1-25 that have ALL unique digits, then subtract from 25.

Key paths through the recursion:

Path 1: Building number "12"

  • Position 0: Place digit 1
    • mask = 0000000010 (bit 1 set), lead = False, limit = False (1 < 2)
  • Position 1: Place digit 2
    • Check: Is digit 2 used? (mask >> 2) & 1 = 0 ✓ Available
    • mask = 0000000110 (bits 1,2 set)
  • Position 2: End reached, return 1 (valid number with unique digits)

Path 2: Attempting "22"

  • Position 0: Place digit 2
    • mask = 0000000100 (bit 2 set), lead = False, limit = True (2 = 2)
  • Position 1: Try digit 2
    • Check: Is digit 2 used? (mask >> 2) & 1 = 1 ✗ Already used!
    • This branch returns 0 (cannot form valid number)

Path 3: Building "7" (single digit)

  • Position 0: Place digit 0 (leading zero)
    • mask = 0 (unchanged), lead = True, limit = False
  • Position 1: Place digit 7
    • mask = 0010000000 (bit 7 set), lead = False
  • Position 2: End reached, return 1

Results Summary:

  • Numbers 1-25 with unique digits: 1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19,20,21,23,24,25
  • Count = 23
  • Numbers with repeated digits = 25 - 23 = 2 (which are 11 and 22)

The algorithm efficiently explores all valid combinations using memoization to avoid recalculating identical states.

Solution Implementation

1class Solution:
2    def numDupDigitsAtMostN(self, n: int) -> int:
3        """
4        Count how many positive integers <= n have at least one repeated digit.
5      
6        Strategy: Count numbers without repeated digits, then subtract from n.
7        Uses digit DP with states tracking position, used digits, leading zeros, and upper bound.
8        """
9        from functools import cache
10      
11        # Convert n to string for digit-by-digit processing
12        digits_str = str(n)
13      
14        @cache
15        def count_unique_digit_numbers(
16            position: int, 
17            used_digits_mask: int, 
18            has_leading_zeros: bool, 
19            is_bounded: bool
20        ) -> int:
21            """
22            Count numbers with unique digits using digit DP.
23          
24            Args:
25                position: Current digit position being processed
26                used_digits_mask: Bitmask representing which digits (0-9) have been used
27                has_leading_zeros: True if we haven't placed a non-zero digit yet
28                is_bounded: True if we must respect the upper bound (digits of n)
29          
30            Returns:
31                Count of valid numbers from this state
32            """
33            # Base case: processed all digits
34            if position >= len(digits_str):
35                # Return 1 if we've formed a valid number (not just leading zeros)
36                return 0 if has_leading_zeros else 1
37          
38            # Determine the maximum digit we can place at this position
39            max_digit = int(digits_str[position]) if is_bounded else 9
40          
41            total_count = 0
42          
43            # Try each possible digit at current position
44            for digit in range(max_digit + 1):
45                # Handle leading zeros case
46                if has_leading_zeros and digit == 0:
47                    # Continue with leading zeros, no longer bounded
48                    total_count += count_unique_digit_numbers(
49                        position + 1, 
50                        used_digits_mask, 
51                        True, 
52                        False
53                    )
54                # Check if digit hasn't been used yet (bit is 0 in mask)
55                elif (used_digits_mask >> digit) & 1 == 0:
56                    # Mark digit as used and continue
57                    new_mask = used_digits_mask | (1 << digit)
58                    # Stay bounded only if we're currently bounded AND using max digit
59                    new_bounded = is_bounded and (digit == max_digit)
60                  
61                    total_count += count_unique_digit_numbers(
62                        position + 1, 
63                        new_mask, 
64                        False, 
65                        new_bounded
66                    )
67          
68            return total_count
69      
70        # Count numbers without repeated digits from 1 to n
71        unique_digit_count = count_unique_digit_numbers(0, 0, True, True)
72      
73        # Numbers with repeated digits = total numbers - numbers without repeated digits
74        return n - unique_digit_count
75
1class Solution {
2    private char[] digitArray;
3    private Integer[][] memoization;
4
5    public int numDupDigitsAtMostN(int n) {
6        // Convert number to character array for digit-by-digit processing
7        digitArray = String.valueOf(n).toCharArray();
8        // Memoization array: [position][bitmask of used digits]
9        // bitmask has 10 bits, each representing whether digit 0-9 has been used
10        memoization = new Integer[digitArray.length][1 << 10];
11        // Total numbers with duplicate digits = n - numbers with all unique digits
12        return n - countUniqueDigitNumbers(0, 0, true, true);
13    }
14
15    /**
16     * Count numbers with all unique digits using digit DP
17     * 
18     * @param position Current position in the number being formed (0-indexed)
19     * @param usedDigitsMask Bitmask representing which digits (0-9) have been used
20     * @param hasLeadingZeros True if we haven't placed any non-zero digit yet
21     * @param isBoundedByInput True if current number is still bounded by input n
22     * @return Count of valid numbers with unique digits from current state
23     */
24    private int countUniqueDigitNumbers(int position, int usedDigitsMask, 
25                                       boolean hasLeadingZeros, boolean isBoundedByInput) {
26        // Base case: reached end of number
27        if (position >= digitArray.length) {
28            // If still has leading zeros, it means we formed number 0, which doesn't count
29            return hasLeadingZeros ? 0 : 1;
30        }
31      
32        // Use memoization only when not bounded and no leading zeros
33        // (these states can be reused)
34        if (!hasLeadingZeros && !isBoundedByInput && memoization[position][usedDigitsMask] != null) {
35            return memoization[position][usedDigitsMask];
36        }
37      
38        // Determine upper bound for current digit
39        int upperBound = isBoundedByInput ? digitArray[position] - '0' : 9;
40        int count = 0;
41      
42        // Try each possible digit at current position
43        for (int digit = 0; digit <= upperBound; ++digit) {
44            if (hasLeadingZeros && digit == 0) {
45                // Skip leading zero, don't mark 0 as used
46                count += countUniqueDigitNumbers(position + 1, usedDigitsMask, 
47                                                true, false);
48            } else if ((usedDigitsMask >> digit & 1) == 0) {
49                // Digit hasn't been used, so we can use it
50                // Mark digit as used by setting its bit in the mask
51                count += countUniqueDigitNumbers(position + 1, 
52                                                usedDigitsMask | (1 << digit), 
53                                                false, 
54                                                isBoundedByInput && digit == upperBound);
55            }
56            // If digit was already used, skip it (don't add to count)
57        }
58      
59        // Store result in memoization table for reusable states
60        if (!hasLeadingZeros && !isBoundedByInput) {
61            memoization[position][usedDigitsMask] = count;
62        }
63      
64        return count;
65    }
66}
67
1class Solution {
2public:
3    int numDupDigitsAtMostN(int n) {
4        // Convert number to string for digit-by-digit processing
5        string numStr = to_string(n);
6        int numDigits = numStr.size();
7      
8        // DP memoization table: dp[position][used_digits_bitmask]
9        // -1 indicates unvisited state
10        int dp[numDigits][1 << 10];
11        memset(dp, -1, sizeof(dp));
12      
13        // Recursive function to count numbers without repeated digits
14        // Parameters:
15        // - pos: current position in the number (0-indexed from left)
16        // - usedDigitsMask: bitmask representing which digits (0-9) have been used
17        // - isLeadingZero: true if we're still in leading zeros
18        // - isTight: true if we're still bounded by the original number's digits
19        auto countUniqueDigitNumbers = [&](this auto&& countUniqueDigitNumbers, 
20                                           int pos, 
21                                           int usedDigitsMask, 
22                                           bool isLeadingZero, 
23                                           bool isTight) -> int {
24            // Base case: reached the end of the number
25            if (pos >= numDigits) {
26                // Return 1 if we formed a valid number (not all leading zeros), 0 otherwise
27                return isLeadingZero ? 0 : 1;
28            }
29          
30            // Check memoization (only valid when not in special states)
31            if (!isLeadingZero && !isTight && dp[pos][usedDigitsMask] != -1) {
32                return dp[pos][usedDigitsMask];
33            }
34          
35            // Determine the upper bound for current digit
36            int upperBound = isTight ? (numStr[pos] - '0') : 9;
37            int count = 0;
38          
39            // Try each possible digit at current position
40            for (int digit = 0; digit <= upperBound; ++digit) {
41                if (isLeadingZero && digit == 0) {
42                    // Still in leading zeros, don't mark 0 as used
43                    count += countUniqueDigitNumbers(pos + 1, 
44                                                     usedDigitsMask, 
45                                                     true, 
46                                                     isTight && (digit == upperBound));
47                } else if ((usedDigitsMask >> digit & 1) == 0) {
48                    // Digit hasn't been used yet, so we can use it
49                    count += countUniqueDigitNumbers(pos + 1, 
50                                                     usedDigitsMask | (1 << digit), 
51                                                     false, 
52                                                     isTight && (digit == upperBound));
53                }
54                // If digit was already used, skip it (no repeated digits allowed)
55            }
56          
57            // Store result in memoization table (only for non-special states)
58            if (!isLeadingZero && !isTight) {
59                dp[pos][usedDigitsMask] = count;
60            }
61          
62            return count;
63        };
64      
65        // Total numbers from 0 to n minus numbers without repeated digits
66        // gives us numbers with repeated digits
67        return n - countUniqueDigitNumbers(0, 0, true, true);
68    }
69};
70
1/**
2 * Counts the number of positive integers with at least one repeated digit
3 * in the range [1, n]
4 * @param n - The upper bound of the range
5 * @returns The count of numbers with repeated digits
6 */
7function numDupDigitsAtMostN(n: number): number {
8    // Convert number to string to process digit by digit
9    const numberString: string = n.toString();
10    const digitCount: number = numberString.length;
11  
12    // Memoization table: dp[position][bitmask]
13    // -1 indicates uncomputed state
14    const memoTable: number[][] = Array.from(
15        { length: digitCount }, 
16        () => Array(1 << 10).fill(-1)
17    );
18
19    /**
20     * Dynamic programming function to count valid numbers without repeated digits
21     * @param position - Current digit position being processed
22     * @param usedDigitsMask - Bitmask representing which digits have been used
23     * @param hasLeadingZeros - Whether we're still in leading zeros
24     * @param isTight - Whether we're still bounded by the original number's digits
25     * @returns Count of valid numbers from this state
26     */
27    const countUniqueDigitNumbers = (
28        position: number, 
29        usedDigitsMask: number, 
30        hasLeadingZeros: boolean, 
31        isTight: boolean
32    ): number => {
33        // Base case: processed all digits
34        if (position >= digitCount) {
35            // Return 1 if we formed a valid number (not just leading zeros)
36            return hasLeadingZeros ? 0 : 1;
37        }
38      
39        // Check memoization for non-leading-zero, non-tight states
40        if (!hasLeadingZeros && !isTight && memoTable[position][usedDigitsMask] !== -1) {
41            return memoTable[position][usedDigitsMask];
42        }
43      
44        // Determine the maximum digit we can place at current position
45        const maxDigit: number = isTight ? parseInt(numberString[position]) : 9;
46        let validCount: number = 0;
47      
48        // Try each possible digit at current position
49        for (let digit = 0; digit <= maxDigit; digit++) {
50            if (hasLeadingZeros && digit === 0) {
51                // Continue with leading zeros
52                validCount += countUniqueDigitNumbers(
53                    position + 1, 
54                    usedDigitsMask, 
55                    true, 
56                    isTight && digit === maxDigit
57                );
58            } else if (((usedDigitsMask >> digit) & 1) === 0) {
59                // Digit hasn't been used yet, so we can use it
60                validCount += countUniqueDigitNumbers(
61                    position + 1, 
62                    usedDigitsMask | (1 << digit), 
63                    false, 
64                    isTight && digit === maxDigit
65                );
66            }
67        }
68      
69        // Memoize result for non-leading-zero, non-tight states
70        if (!hasLeadingZeros && !isTight) {
71            memoTable[position][usedDigitsMask] = validCount;
72        }
73      
74        return validCount;
75    };
76
77    // Total numbers with repeated digits = n - (numbers without repeated digits)
78    return n - countUniqueDigitNumbers(0, 0, true, true);
79}
80

Time and Space Complexity

Time Complexity: O(D * 2^10 * D) where D is the number of digits in n.

The time complexity can be analyzed through the memoization states and transitions:

  • Parameter i ranges from 0 to D-1 (number of digits)
  • Parameter mask can have at most 2^10 different values (representing which digits 0-9 have been used)
  • Parameter lead has 2 possible values (True/False)
  • Parameter limit has 2 possible values (True/False)

This gives us at most D * 2^10 * 2 * 2 = O(D * 2^10) unique states. For each state, we iterate through at most 10 digits (0-9), making the total time complexity O(D * 2^10 * 10). Since 2^10 = 1024 and we multiply by 10, this simplifies to O(D * 10240) or O(D * 2^10 * D) in terms of digit operations.

Space Complexity: O(D * 2^10)

The space complexity comes from:

  • The recursion stack depth which is at most D (the number of digits)
  • The memoization cache storing up to D * 2^10 * 2 * 2 states
  • The string representation of n which takes O(D) space

The dominant factor is the memoization cache, giving us O(D * 2^10) space complexity.

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

Common Pitfalls

1. Incorrect Handling of Leading Zeros in the Bitmask

The Pitfall: A common mistake is to mark the digit 0 as "used" when it appears as a leading zero. This would incorrectly prevent valid numbers like 101, 202, etc., from being counted.

Why it happens: When we encounter leading zeros, we might intuitively think to mark 0 as used in the bitmask. However, leading zeros don't actually represent the digit 0 in the number - they're just placeholders.

Incorrect approach:

for digit in range(max_digit + 1):
    if has_leading_zeros and digit == 0:
        # WRONG: marking 0 as used for leading zeros
        total_count += count_unique_digit_numbers(
            position + 1, 
            used_digits_mask | (1 << 0),  # ❌ Incorrectly setting bit 0
            True, 
            False
        )

Correct approach:

for digit in range(max_digit + 1):
    if has_leading_zeros and digit == 0:
        # CORRECT: Don't mark 0 as used for leading zeros
        total_count += count_unique_digit_numbers(
            position + 1, 
            used_digits_mask,  # ✓ Keep mask unchanged
            True, 
            False
        )

2. Misunderstanding the Limit Flag Update

The Pitfall: Incorrectly updating the is_bounded flag, particularly forgetting that once we place a digit smaller than the maximum allowed, all subsequent positions become unbounded.

Common mistakes:

  • Setting is_bounded = True for all recursive calls when currently bounded
  • Not considering that leading zeros automatically make the number unbounded

Incorrect approach:

# WRONG: Always passing the current bounded state
new_bounded = is_bounded  # ❌ Doesn't account for digit choice
total_count += count_unique_digit_numbers(
    position + 1, new_mask, False, new_bounded
)

Correct approach:

# CORRECT: Only stay bounded if we use the maximum digit
new_bounded = is_bounded and (digit == max_digit)  # ✓
total_count += count_unique_digit_numbers(
    position + 1, new_mask, False, new_bounded
)

3. Off-by-One Error in Final Calculation

The Pitfall: The problem asks for numbers from 1 to n with repeated digits, but the digit DP counts all valid numbers including 0. This can lead to confusion about whether to include or exclude zero.

Why it matters: The function count_unique_digit_numbers doesn't generate the number 0 when starting with has_leading_zeros = True because the base case returns 0 for pure leading zeros. This is correct behavior, but it's easy to misunderstand and try to "fix" it.

Incorrect attempts to handle this:

# WRONG: Trying to adjust for zero
return n - unique_digit_count + 1  # ❌ Overcounting

# WRONG: Starting count from 1
return (n - 1) - unique_digit_count  # ❌ Undercounting

Correct approach:

# CORRECT: Direct subtraction works because:
# - n represents count of numbers from 1 to n
# - unique_digit_count represents numbers from 1 to n with unique digits
return n - unique_digit_count  # ✓

4. Bit Manipulation Confusion

The Pitfall: Misunderstanding the bit operations for checking and setting digits in the mask.

Common mistakes:

# WRONG: Incorrect check for unused digit
if used_digits_mask & (1 << digit) == 0:  # Works but less clear
  
# WRONG: Using wrong operator precedence
if used_digits_mask >> digit & 1 ^ 1:  # Can be confusing without parentheses

# WRONG: Checking for used digit instead of unused
if (used_digits_mask >> digit) & 1 == 1:  # ❌ Logic reversed

Correct and clear approach:

# CORRECT: Check if digit is NOT used (bit is 0)
if (used_digits_mask >> digit) & 1 == 0:  # ✓ Clear and correct
    # Mark digit as used
    new_mask = used_digits_mask | (1 << digit)

These pitfalls often lead to subtle bugs that can be hard to debug because they only affect certain edge cases or ranges of numbers.

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 the following tree as input?

1def serialize(root):
2    res = []
3    def dfs(root):
4        if not root:
5            res.append('x')
6            return
7        res.append(root.val)
8        dfs(root.left)
9        dfs(root.right)
10    dfs(root)
11    return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4    StringJoiner res = new StringJoiner(" ");
5    serializeDFS(root, res);
6    return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10    if (root == null) {
11        result.add("x");
12        return;
13    }
14    result.add(Integer.toString(root.val));
15    serializeDFS(root.left, result);
16    serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2    let res = [];
3    serialize_dfs(root, res);
4    return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8    if (!root) {
9        res.push("x");
10        return;
11    }
12    res.push(root.val);
13    serialize_dfs(root.left, res);
14    serialize_dfs(root.right, res);
15}
16

Recommended Readings

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

Load More