title: Palindrome Number
slug: palindrome-number
difficulty: easy
leetcode_id: 9
leetcode_url: https://leetcode.com/problems/palindrome-number/
categories:
  - math
patterns:
  - two-pointers

function_signature: "def is_palindrome(x: int) -> bool:"

test_cases:
  visible:
    - input: { x: 121 }
      expected: true
    - input: { x: -121 }
      expected: false
    - input: { x: 10 }
      expected: false
  hidden:
    - input: { x: 0 }
      expected: true
    - input: { x: 12321 }
      expected: true
    - input: { x: 1234 }
      expected: false
    - input: { x: 11 }
      expected: true
    - input: { x: -1 }
      expected: false

description: |
  Given an integer `x`, return `true` if `x` is a **palindrome**, and `false` otherwise.

  A **palindrome** is a number that reads the same backward as forward. For example, `121` is a palindrome while `123` is not.

constraints: |
  - `-2^31 <= x <= 2^31 - 1`

examples:
  - input: "x = 121"
    output: "true"
    explanation: "121 reads as 121 from left to right and from right to left."
  - input: "x = -121"
    output: "false"
    explanation: "From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome."
  - input: "x = 10"
    output: "false"
    explanation: "Reads 01 from right to left. Therefore it is not a palindrome."

explanation:
  intuition: |
    Think of a palindrome like a mirror: the left half reflects perfectly onto the right half.

    For a number like `12321`, if you cover the middle digit and compare the left side (`12`) with the reversed right side (`12`), they match. The key insight is that we don't need to reverse the entire number — we only need to reverse **half** of it and compare.

    Why reverse only half? Two reasons:
    1. **Efficiency**: We avoid potential integer overflow that could occur when reversing large numbers
    2. **Elegance**: Once we've processed half the digits, we have enough information to determine if it's a palindrome

    The clever trick is knowing when we've reached the halfway point: when the reversed half becomes greater than or equal to the remaining original number, we've crossed the middle.

  approach: |
    We solve this by **reversing the second half** of the number and comparing it to the first half:

    **Step 1: Handle edge cases**

    - Negative numbers are never palindromes (the minus sign doesn't mirror)
    - Numbers ending in `0` (except `0` itself) are never palindromes — they would need a leading zero

    &nbsp;

    **Step 2: Reverse the second half digit by digit**

    - Extract the last digit using `x % 10`
    - Build the reversed number: `reversed = reversed * 10 + last_digit`
    - Remove the last digit from original: `x = x // 10`
    - Continue until `reversed >= x` (we've reached or passed the middle)

    &nbsp;

    **Step 3: Compare the two halves**

    - For even-length numbers: `x == reversed` (e.g., `1221` → `x=12`, `reversed=12`)
    - For odd-length numbers: `x == reversed // 10` (e.g., `12321` → `x=12`, `reversed=123`, ignore middle digit)

    &nbsp;

    This approach elegantly handles both even and odd length numbers without special cases.

  common_pitfalls:
    - title: Converting to String
      description: |
        The most intuitive approach is converting the number to a string and checking if it equals its reverse:

        ```python
        return str(x) == str(x)[::-1]
        ```

        While this works and is O(n), it uses O(n) extra space for the string and involves type conversion overhead. The follow-up challenge asks us to solve it without string conversion.
      wrong_approach: "String conversion and reversal"
      correct_approach: "Reverse half the number mathematically"

    - title: Integer Overflow When Reversing
      description: |
        If you try to reverse the entire number, you might overflow for values near `2^31`. For example, reversing `2147483647` would give `7463847412`, which exceeds the 32-bit integer limit.

        By only reversing half the number, we guarantee the reversed portion stays within bounds since it's at most half the size of the original.
      wrong_approach: "Reverse the entire number"
      correct_approach: "Reverse only half to avoid overflow"

    - title: Forgetting Numbers Ending in Zero
      description: |
        Numbers like `10`, `100`, `1230` end in zero. For these to be palindromes, they'd need to start with zero — but no positive integer starts with zero.

        The only exception is `0` itself, which is a palindrome. Check for `x % 10 == 0 and x != 0` as an early exit.
      wrong_approach: "Missing the trailing zero check"
      correct_approach: "Return false early for non-zero numbers ending in 0"

  key_takeaways:
    - "**Half-reversal technique**: When checking symmetry, you often only need to process half the data"
    - "**Overflow prevention**: Reversing only half of a number keeps values safely within integer bounds"
    - "**Edge cases matter**: Negative numbers and trailing zeros need explicit handling"
    - "**Math over strings**: Many digit-manipulation problems have elegant mathematical solutions that avoid string conversion overhead"

  time_complexity: "O(log n). We process half the digits, and a number has log₁₀(n) digits."
  space_complexity: "O(1). We only use a fixed number of variables regardless of input size."

solutions:
  - approach_name: Reverse Half Number
    is_optimal: true
    code: |
      def is_palindrome(x: int) -> bool:
          # Negative numbers and numbers ending in 0 (except 0 itself) aren't palindromes
          if x < 0 or (x % 10 == 0 and x != 0):
              return False

          reversed_half = 0

          # Build reversed half until it's >= remaining number
          while x > reversed_half:
              # Extract last digit and add to reversed half
              reversed_half = reversed_half * 10 + x % 10
              # Remove last digit from original
              x //= 10

          # Even length: x == reversed_half (e.g., 1221)
          # Odd length: x == reversed_half // 10 (e.g., 12321, middle digit in reversed_half)
          return x == reversed_half or x == reversed_half // 10
    explanation: |
      **Time Complexity:** O(log n) — We process roughly half the digits.

      **Space Complexity:** O(1) — Only two integer variables used.

      We reverse only the second half of the number and compare. The loop terminates when the reversed portion is at least as large as the remaining original, indicating we've reached the middle. The two conditions in the return handle both even-length and odd-length palindromes.

  - approach_name: String Conversion
    is_optimal: false
    code: |
      def is_palindrome(x: int) -> bool:
          # Convert to string and compare with reverse
          s = str(x)
          return s == s[::-1]
    explanation: |
      **Time Complexity:** O(n) — String creation and reversal each take O(n) where n is the number of digits.

      **Space Complexity:** O(n) — The string representation requires space proportional to the number of digits.

      This is the most straightforward approach: convert to string and check if it equals its reverse. While simple and readable, it uses extra space and doesn't meet the follow-up challenge of avoiding string conversion.

  - approach_name: Full Number Reversal
    is_optimal: false
    code: |
      def is_palindrome(x: int) -> bool:
          # Negative numbers are not palindromes
          if x < 0:
              return False

          original = x
          reversed_num = 0

          # Reverse the entire number
          while x > 0:
              reversed_num = reversed_num * 10 + x % 10
              x //= 10

          return original == reversed_num
    explanation: |
      **Time Complexity:** O(log n) — We process all digits once.

      **Space Complexity:** O(1) — Only integer variables used.

      This approach reverses the entire number and compares with the original. While it works for most cases, it can cause integer overflow for very large numbers near the 32-bit limit. The half-reversal approach is preferred as it avoids this risk.
