AlgoAtlas

Math And Geometry

Phase 3 13 solutions

Math & Geometry

mindmap
  root((Math & Geometry))
    Matrix Operations
      Transpose Matrix LC867
      Rotate Image LC48
      Spiral Matrix LC54
      Set Matrix Zeroes LC73
    Number Theory
      GCD of Strings LC1071
      Insert GCD in Linked List LC2807
      Happy Number LC202
      Pow x n LC50
      Detect Squares LC2013
    Simulation
      Plus One LC66
      Roman to Integer LC13
      Excel Sheet Column Title LC168
    String Math
      Multiply Strings LC43
      Add Binary LC67

Problem List

# Problem LC # Difficulty Key Technique
1 Excel Sheet Column Title 168 Easy Base-26 encoding
2 GCD of Strings 1071 Easy GCD + string divisibility
3 Insert GCD in Linked List 2807 Medium GCD + list manipulation
4 Transpose Matrix 867 Easy Index swap
5 Rotate Image 48 Medium Transpose + reverse
6 Spiral Matrix 54 Medium Boundary simulation
7 Set Matrix Zeroes 73 Medium In-place marking
8 Happy Number 202 Easy Cycle detection (Floyd's)
9 Plus One 66 Easy Carry propagation
10 Roman to Integer 13 Easy Symbol map + lookahead
11 Pow(x, n) 50 Medium Fast exponentiation
12 Multiply Strings 43 Medium Grade-school multiplication
13 Detect Squares 2013 Medium Point counting + geometry

Matrix Operation Patterns

graph TD
    M[Matrix Operations] --> T[Transpose]
    M --> R[Rotate]
    M --> S[Spiral]
    M --> Z[Zero-marking]

    T --> T1["swap matrix[i][j] and matrix[j][i]"]
    T1 --> T2[Transpose Matrix #867]

    R --> R1[Transpose then reverse each row]
    R1 --> R2[Rotate Image #48 — 90° clockwise]

    S --> S1[Shrink boundaries: top, bottom, left, right]
    S1 --> S2[Spiral Matrix #54]

    Z --> Z1["Use first row/col as markers"]
    Z1 --> Z2[Set Matrix Zeroes #73]

Spiral Matrix Traversal Flow

flowchart TD
    A[top=0, bottom=n-1, left=0, right=m-1] --> B{"top <= bottom AND left <= right?"}
    B -- No --> Z[Return result]
    B -- Yes --> C[Traverse top row left to right, top++]
    C --> D[Traverse right col top to bottom, right--]
    D --> E{"top <= bottom?"}
    E -- Yes --> F[Traverse bottom row right to left, bottom--]
    E -- No --> B
    F --> G{"left <= right?"}
    G -- Yes --> H[Traverse left col bottom to top, left++]
    G -- No --> B
    H --> B

    style A fill:#2d6a4f,color:#fff
    style Z fill:#1b4332,color:#fff

Fast Exponentiation Flow

flowchart TD
    A[pow x, n] --> B{"n < 0?"}
    B -- Yes --> C["x = 1/x, n = -n"]
    B -- No --> D[result = 1]
    C --> D
    D --> E{"n > 0?"}
    E -- No --> K[Return result]
    E -- Yes --> F{n is odd?}
    F -- Yes --> G[result *= x]
    F -- No --> H[skip]
    G --> I["x = x * x, n = n // 2"]
    H --> I
    I --> E

    style A fill:#2d6a4f,color:#fff
    style K fill:#1b4332,color:#fff

Time: O(log n) — halve the exponent each iteration


Cycle Detection — Happy Number Flow

flowchart TD
    A[slow = n, fast = n] --> B[Compute next: sum of squares of digits]
    B --> C[slow = next of slow]
    C --> D[fast = next of next of fast]
    D --> E{slow == fast?}
    E -- No --> C
    E -- Yes --> F{slow == 1?}
    F -- Yes --> G[Return True — Happy Number]
    F -- No --> H[Return False — cycle, not happy]

    style G fill:#2d6a4f,color:#fff
    style H fill:#9b2226,color:#fff

Alternative: use a seen set; stop when 1 or repeated


GCD / Number Theory Decision Tree

flowchart TD
    A[Number Theory Problem] --> B{Involves strings?}
    B -- Yes --> C[GCD of Strings #1071]
    C --> D["Check s+t == t+s, then return s[":gcd(len s, len t)"]"]

    B -- No --> E{Involves linked list?}
    E -- Yes --> F[Insert GCD in Linked List #2807]
    F --> G[Between each pair, insert node with gcd of values]

    E -- No --> H{Involves exponentiation?}
    H -- Yes --> I[Pow x,n #50 — fast exponentiation]

    H -- No --> J{Involves digit sum cycle?}
    J -- Yes --> K[Happy Number #202 — Floyd's cycle detection]

    J -- No --> L[Excel Column #168 — base-26 with offset]

Complexity Summary

Problem Time Space
Excel Sheet Column Title O(log n) O(log n)
GCD of Strings O(n + m) O(n + m)
Insert GCD in Linked List O(n log v) O(1)
Transpose Matrix O(n²) O(1)
Rotate Image O(n²) O(1)
Spiral Matrix O(n·m) O(1)
Set Matrix Zeroes O(n·m) O(1)
Happy Number O(log n) O(1)
Plus One O(n) O(1)
Roman to Integer O(n) O(1)
Pow(x, n) O(log n) O(1)
Multiply Strings O(n·m) O(n+m)
Detect Squares O(n) per query O(n)

Study Order

  1. Plus One — carry propagation warm-up
  2. Roman to Integer — symbol map
  3. Excel Sheet Column Title — base conversion
  4. Happy Number — cycle detection
  5. GCD of Strings — number theory + strings
  6. Transpose Matrix — matrix basics
  7. Rotate Image — transpose + reverse
  8. Spiral Matrix — boundary simulation
  9. Set Matrix Zeroes — in-place marking
  10. Pow(x, n) — fast exponentiation
  11. Multiply Strings — grade-school algorithm
  12. Insert GCD in Linked List — GCD + list
  13. Detect Squares — geometry + counting

Math & Geometry

Core Intuition

These problems rely on mathematical properties or careful simulation. Key skills: modular arithmetic, GCD/LCM, number base conversion, matrix traversal patterns, and detecting cycles.

  • Matrix ops: In-place rotation/transpose uses index math; spiral uses boundary pointers
  • Number theory: GCD via Euclidean algorithm; cycle detection via Floyd's or set
  • Simulation: Follow the rules exactly; boundary conditions are the hard part
  • Base conversion: Repeated division/modulo

Decision Flowchart

flowchart TD
    A["Math/Geometry Problem"] --> B{Problem type?}
    B -->|Matrix traversal| C{Pattern?}
    C -->|Layer by layer| D["Rotate Image<br/>in-place 4-way swap"]
    C -->|Boundary shrink| E["Spiral Matrix<br/>four pointers"]
    C -->|Row/col zeroing| F["Set Matrix Zeroes<br/>flag first row/col"]
    C -->|Simple transpose| G["Transpose<br/>swap i,j"]
    B -->|Number properties| H{Operation?}
    H -->|Digit manipulation| I["Plus One, Reverse Integer<br/>handle carry/overflow"]
    H -->|Cycle detection| J["Happy Number<br/>Floyd's or set"]
    H -->|GCD based| K["GCD Strings, Insert GCD LL<br/>Euclidean algorithm"]
    H -->|Fast exponentiation| L["Pow<br/>binary exponentiation"]
    B -->|String arithmetic| M["Multiply Strings<br/>grade-school multiplication"]
    B -->|Numeral systems| N["Excel Column, Roman to Int<br/>mapping + iteration"]
    B -->|Geometry / counting| O["Detect Squares<br/>count points by coordinate"]

Problems

Excel Sheet Column Title — LC 168 | Easy

  • Learn: Base-26 but 1-indexed (no zero); subtract 1 before each modulo to shift A=1 to A=0
  • Mistake: Treating as standard base-26 (fails for Z, AZ, etc.)
  • Complexity: O(log n) time, O(log n) space
  • Edge: n=1 → "A"; n=26 → "Z"; n=27 → "AA"

GCD of Strings — LC 1071 | Easy

  • Learn: GCD string divides both; check s+t == t+s (necessary condition); answer is s[:gcd(len(s),len(t))]
  • Mistake: Manually checking divisibility instead of using the concatenation trick
  • Complexity: O(m+n) time, O(m+n) space
  • Edge: No common divisor → ""; one string is the other → shorter string

Insert Greatest Common Divisors in Linked List — LC 2807 | Medium

  • Learn: Traverse pairs of adjacent nodes; insert new node with gcd(a.val, b.val) between them
  • Mistake: Losing the reference to next node before inserting
  • Complexity: O(n·log(max_val)) time, O(1) space (excluding output)
  • Edge: Single node list → no insertions; two nodes → one insertion

Transpose Matrix — LC 867 | Easy

  • Learn: result[j][i] = matrix[i][j]; handle non-square matrices (rows/cols swap)
  • Mistake: In-place transpose on non-square matrix (dimensions change)
  • Complexity: O(m·n) time, O(m·n) space
  • Edge: 1×n → n×1; already square

Rotate Image — LC 48 | Medium

  • Learn: Transpose then reverse each row (90° clockwise); or 4-way swap layer by layer
  • Mistake: Using extra space; doing operations in wrong order (reverse then transpose = counter-clockwise)
  • Complexity: O(n²) time, O(1) space
  • Edge: 1×1 matrix → no change; 2×2 matrix

Spiral Matrix — LC 54 | Medium

  • Learn: Four boundary pointers (top, bottom, left, right); shrink after each direction pass
  • Mistake: Not checking if rows/cols remain before inner passes (causes duplicates)
  • Complexity: O(m·n) time, O(1) space (excluding output)
  • Edge: Single row; single column; 1×1 matrix

Set Matrix Zeroes — LC 73 | Medium

  • Learn: Use first row and first column as flags; handle them separately with two boolean variables
  • Mistake: Using O(m+n) extra space; zeroing rows/cols immediately (corrupts flags)
  • Complexity: O(m·n) time, O(1) space
  • Edge: First row or first column contains a zero (need separate flags)

Happy Number — LC 202 | Easy

  • Learn: Sum of squares of digits; detect cycle with Floyd's two-pointer or a set
  • Mistake: Infinite loop without cycle detection
  • Complexity: O(log n) time per step, O(log n) space (set) or O(1) (Floyd's)
  • Edge: n=1 → true; n=7 → true (first non-1 happy number)

Plus One — LC 66 | Easy

  • Learn: Traverse from right; handle carry; if all 9s, prepend 1
  • Mistake: Not handling the all-9s case (need to expand array)
  • Complexity: O(n) time, O(n) space worst case
  • Edge: [9] → [1,0]; [9,9,9] → [1,0,0,0]

Roman to Integer — LC 13 | Easy

  • Learn: Map each symbol; if current value < next value, subtract; else add
  • Mistake: Processing left-to-right without lookahead for subtractive cases
  • Complexity: O(n) time, O(1) space
  • Edge: Single character; "IV", "IX", "XL", "XC", "CD", "CM"

Pow(x, n) — LC 50 | Medium

  • Learn: Binary exponentiation: if n is even, x^n = (x²)^(n/2); handle negative n
  • Mistake: Linear recursion O(n); not handling n < 0 (return 1/pow(x,-n))
  • Complexity: O(log n) time, O(log n) space (recursion) or O(1) iterative
  • Edge: n=0 → 1; x=0 and n<0 (undefined, but LC guarantees valid); n=INT_MIN (overflow on negation)

Multiply Strings — LC 43 | Medium

  • Learn: Grade-school multiplication; result[i+j+1] += num1[i] × num2[j]; handle carry in second pass
  • Mistake: Converting to int (defeats the purpose; overflow for large inputs)
  • Complexity: O(m·n) time, O(m+n) space
  • Edge: "0" × anything → "0"; single digit inputs

Detect Squares — LC 2013 | Medium

  • Learn: Store point counts in a dict; for each query point and each point sharing x-coord, find the square's other two corners
  • Mistake: Checking all pairs of points (O(n²) per query); use the diagonal point to determine the square
  • Complexity: O(n) add, O(n) query (n = distinct x-coord points), O(n) space
  • Edge: Fewer than 3 points → 0 squares; duplicate points (count them); query point already in set

General Edge Cases

  • Integer overflow (Pow with large n, Multiply Strings)
  • Negative numbers (Reverse Integer, Pow)
  • Zero inputs (Multiply Strings "0", Happy Number)
  • Non-square matrices (Transpose)
  • Empty matrix
  • Single element matrix/array
# Problem LC # Difficulty Key Technique
1 Excel Sheet Column Title 168 Easy Base-26 encoding
2 GCD of Strings 1071 Easy GCD + string divisibility
3 Insert GCD in Linked List 2807 Medium GCD + list manipulation
4 Transpose Matrix 867 Easy Index swap
5 Rotate Image 48 Medium Transpose + reverse
6 Spiral Matrix 54 Medium Boundary simulation
7 Set Matrix Zeroes 73 Medium In-place marking
8 Happy Number 202 Easy Cycle detection (Floyd's)
9 Plus One 66 Easy Carry propagation
10 Roman to Integer 13 Easy Symbol map + lookahead
11 Pow(x, n) 50 Medium Fast exponentiation
12 Multiply Strings 43 Medium Grade-school multiplication
13 Detect Squares 2013 Medium Point counting + geometry
Detect Squares ▼ expand
"""
# 2013. Detect Squares

You are given a stream of points on the X-Y plane. Design an algorithm that:
- **Adds** new points from the stream into a data structure.
- **Counts** the number of ways to choose three points from the data structure such that the
  three points and a given query point form an **axis-aligned square** with **positive area**.

An axis-aligned square has all four sides parallel to the axes and all four sides equal in length.

Implement the `DetectSquares` class:
- `DetectSquares()` Initializes the object with an empty data structure.
- `void add(int[] point)` Adds a new point `point = [x, y]` to the data structure.
- `int count(int[] point)` Counts the number of ways to form axis-aligned squares with `point`.

## Examples

```text
Input:
["DetectSquares", "add", "add", "add", "count", "count", "add", "count"]
[[], [[3,10]], [[11,2]], [[3,2]], [[11,10]], [[14,8]], [[11,2]], [[11,10]]]
Output:
[null, null, null, null, 1, 0, null, 2]
```

## Constraints
- point.length == 2
- 0 <= point[x], point[y] <= 1000
- At most 3000 calls will be made to add and count.
"""

from collections import defaultdict
from typing import List


class DetectSquares:
    def __init__(self):
        self.pt_count = defaultdict(int)   # (x,y) -> count
        self.x_to_ys = defaultdict(set)    # x -> set of y values

    def add(self, point: List[int]) -> None:
        # Increment count of this point in the frequency map
        # O(1) time — update frequency map and x->y index
        x, y = point
        self.pt_count[(x, y)] += 1
        self.x_to_ys[x].add(y)

    def count(self, point: List[int]) -> int:
        # For each point sharing the same x as query, try it as diagonal corner
        # The other two corners are determined; check if they exist in the map
        # O(n) time — for each diagonal point, check if square can be completed
        qx, qy = point
        result = 0
        for y in self.x_to_ys[qx]:
            if y == qy:
                continue
            side = abs(y - qy)
            for dx in (qx + side, qx - side):
                result += self.pt_count[(qx, y)] * self.pt_count[(dx, qy)] * self.pt_count[(dx, y)]
        return result


if __name__ == '__main__':
    ds = DetectSquares()
    ds.add([3, 10]); ds.add([11, 2]); ds.add([3, 2])
    assert ds.count([11, 10]) == 1
    assert ds.count([14, 8]) == 0
    ds.add([11, 2])
    assert ds.count([11, 10]) == 2
    print("All tests passed.")
Excel Sheet Column Title ▼ expand
"""
# 168. Excel Sheet Column Title

Given an integer `columnNumber`, return its corresponding column title as it appears in an Excel sheet.

```text
A -> 1
B -> 2
...
Z -> 26
AA -> 27
AB -> 28
...
```

## Examples

```text
Input: columnNumber = 1
Output: "A"

Input: columnNumber = 28
Output: "AB"

Input: columnNumber = 701
Output: "ZY"
```

## Constraints
- 1 <= columnNumber <= 2^31 - 1
"""

class Solution:
    def convertToTitle(self, columnNumber: int) -> str:
        # Base-26 conversion but 1-indexed (A=1, Z=26, AA=27)
        # Subtract 1 before modulo to handle the 1-indexed offset
        # O(log n) time, O(log n) space for result string
        result = []
        while columnNumber:
            columnNumber -= 1
            result.append(chr(columnNumber % 26 + ord('A')))
            columnNumber //= 26
        return ''.join(reversed(result))


if __name__ == '__main__':
    s = Solution()
    assert s.convertToTitle(1) == "A"
    assert s.convertToTitle(26) == "Z"
    assert s.convertToTitle(28) == "AB"
    assert s.convertToTitle(701) == "ZY"
    print("All tests passed.")
Greatest Common Divisor Of Strings ▼ expand
"""
# 1071. Greatest Common Divisor of Strings

For two strings `s` and `t`, we say "t divides s" if and only if `s = t + t + ... + t`
(i.e., `t` is concatenated with itself one or more times).

Given two strings `str1` and `str2`, return the largest string `x` such that `x` divides both `str1` and `str2`.

## Examples

```text
Input: str1 = "ABCABC", str2 = "ABC"
Output: "ABC"

Input: str1 = "ABABAB", str2 = "ABAB"
Output: "AB"

Input: str1 = "LEET", str2 = "CODE"
Output: ""
```

## Constraints
- 1 <= str1.length, str2.length <= 1000
- str1 and str2 consist of English uppercase letters.
"""

from math import gcd


class Solution:
    def gcdOfStrings_brute(self, str1: str, str2: str) -> str:
        # O(n^2) time — check every prefix of the shorter string
        shorter, longer = (str1, str2) if len(str1) <= len(str2) else (str2, str1)
        for length in range(len(shorter), 0, -1):
            t = shorter[:length]
            if len(str1) % length == 0 and len(str2) % length == 0:
                if t * (len(str1) // length) == str1 and t * (len(str2) // length) == str2:
                    return t
        return ""

    def gcdOfStrings(self, str1: str, str2: str) -> str:
        # If str1+str2 == str2+str1, a GCD string exists
        # Its length is gcd(len(str1), len(str2))
        # O(n) time — GCD of lengths gives candidate; verify with concatenation check
        if str1 + str2 != str2 + str1:
            return ""
        return str1[:gcd(len(str1), len(str2))]


if __name__ == '__main__':
    s = Solution()
    for method in (s.gcdOfStrings_brute, s.gcdOfStrings):
        assert method("ABCABC", "ABC") == "ABC"
        assert method("ABABAB", "ABAB") == "AB"
        assert method("LEET", "CODE") == ""
    print("All tests passed.")
Happy Number ▼ expand
"""
# 202. Happy Number

Write an algorithm to determine if a number `n` is happy.

A **happy number** is a number defined by the following process:
- Starting with any positive integer, replace the number by the sum of the squares of its digits.
- Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle that does not include 1.
- Numbers for which this process ends in 1 are happy.

Return `true` if `n` is a happy number, and `false` if not.

## Examples

```text
Input: n = 19
Output: true
Explanation: 1^2 + 9^2 = 82 -> 8^2 + 2^2 = 68 -> ... -> 1

Input: n = 2
Output: false
```

## Constraints
- 1 <= n <= 2^31 - 1
"""


def _sum_of_squares(n: int) -> int:
    total = 0
    while n:
        n, d = divmod(n, 10)
        total += d * d
    return total


class Solution:
    def isHappy_set(self, n: int) -> bool:
        # O(log n) time per step, O(log n) space — hash set to detect cycle
        seen = set()
        while n != 1 and n not in seen:
            seen.add(n)
            n = _sum_of_squares(n)
        return n == 1

    def isHappy(self, n: int) -> bool:
        # Repeatedly sum squares of digits; cycle detection via set or Floyd's
        # If we reach 1, it's happy; if we revisit a number, it's not
        # O(log n) time per step, O(1) space — Floyd's slow/fast cycle detection
        slow, fast = n, _sum_of_squares(n)
        while fast != 1 and slow != fast:
            slow = _sum_of_squares(slow)
            fast = _sum_of_squares(_sum_of_squares(fast))
        return fast == 1


if __name__ == '__main__':
    s = Solution()
    for method in (s.isHappy_set, s.isHappy):
        assert method(19) is True
        assert method(2) is False
        assert method(1) is True
    print("All tests passed.")
Insert Greatest Common Divisors In Linked List ▼ expand
"""
# 2807. Insert Greatest Common Divisors in Linked List

Given the head of a linked list, for every pair of adjacent nodes, insert a new node
with value equal to the greatest common divisor of those two nodes' values.

Return the linked list after insertion.

## Examples

```text
Input: head = [18, 6, 10, 3]
Output: [18, 6, 6, 2, 10, 1, 3]

Input: head = [7]
Output: [7]
```

## Constraints
- The number of nodes in the list is in the range [1, 5000].
- 1 <= Node.val <= 1000
"""

from __future__ import annotations
from math import gcd
from typing import Optional


class ListNode:
    def __init__(self, val: int = 0, next: Optional[ListNode] = None):
        self.val = val
        self.next = next


class Solution:
    def insertGreatestCommonDivisors(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # Between each pair of adjacent nodes, insert a node with gcd(a, b)
        # O(n) time, O(1) space — single pass inserting GCD nodes between each pair
        cur = head
        while cur and cur.next:
            g = gcd(cur.val, cur.next.val)
            cur.next = ListNode(g, cur.next)
            cur = cur.next.next
        return head


def to_list(head: Optional[ListNode]) -> list:
    result = []
    while head:
        result.append(head.val)
        head = head.next
    return result

def from_list(vals: list) -> Optional[ListNode]:
    dummy = ListNode()
    cur = dummy
    for v in vals:
        cur.next = ListNode(v)
        cur = cur.next
    return dummy.next


if __name__ == '__main__':
    s = Solution()
    assert to_list(s.insertGreatestCommonDivisors(from_list([18, 6, 10, 3]))) == [18, 6, 6, 2, 10, 1, 3]
    assert to_list(s.insertGreatestCommonDivisors(from_list([7]))) == [7]
    print("All tests passed.")
Multiply Strings ▼ expand
"""
# 43. Multiply Strings

Given two non-negative integers `num1` and `num2` represented as strings, return the product
of `num1` and `num2`, also represented as a string.

**Note:** You must not use any built-in BigInteger library or convert the inputs to integer directly.

## Examples

```text
Input: num1 = "2", num2 = "3"
Output: "6"

Input: num1 = "123", num2 = "456"
Output: "56088"
```

## Constraints
- 1 <= num1.length, num2.length <= 200
- num1 and num2 consist of digits only.
- Both num1 and num2 do not contain any leading zero, except the number 0 itself.
"""


class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        # Simulate grade-school multiplication digit by digit
        # Result digit at position i+j comes from nums1[i] * nums2[j]
        # Propagate carries after all multiplications
        # O(m*n) time — grade-school digit-by-digit multiplication with position tracking
        if num1 == "0" or num2 == "0":
            return "0"
        m, n = len(num1), len(num2)
        pos = [0] * (m + n)
        for i in range(m - 1, -1, -1):
            for j in range(n - 1, -1, -1):
                mul = (ord(num1[i]) - 48) * (ord(num2[j]) - 48)
                p1, p2 = i + j, i + j + 1
                total = mul + pos[p2]
                pos[p2] = total % 10
                pos[p1] += total // 10
        result = ''.join(str(d) for d in pos).lstrip('0')
        return result or "0"


if __name__ == '__main__':
    s = Solution()
    assert s.multiply("2", "3") == "6"
    assert s.multiply("123", "456") == "56088"
    assert s.multiply("0", "456") == "0"
    assert s.multiply("99", "99") == "9801"
    print("All tests passed.")
Plus One ▼ expand
"""
# 66. Plus One

You are given a **large integer** represented as an integer array `digits`, where each `digits[i]`
is the i-th digit of the integer. The digits are ordered from most significant to least significant.

Increment the large integer by one and return the resulting array of digits.

## Examples

```text
Input: digits = [1,2,3]
Output: [1,2,4]

Input: digits = [4,3,2,1]
Output: [4,3,2,2]

Input: digits = [9]
Output: [1,0]
```

## Constraints
- 1 <= digits.length <= 100
- 0 <= digits[i] <= 9
- digits does not contain any leading 0's.
"""

from typing import List


class Solution:
    def plusOne(self, digits: List[int]) -> List[int]:
        # Add 1 from the last digit; propagate carry leftward
        # If carry remains after all digits, prepend 1
        # O(n) time, O(1) extra space — iterate from end propagating carry
        for i in range(len(digits) - 1, -1, -1):
            if digits[i] < 9:
                digits[i] += 1
                return digits
            digits[i] = 0
        return [1] + digits


if __name__ == '__main__':
    s = Solution()
    assert s.plusOne([1,2,3]) == [1,2,4]
    assert s.plusOne([9]) == [1,0]
    assert s.plusOne([9,9]) == [1,0,0]
    assert s.plusOne([4,3,2,1]) == [4,3,2,2]
    print("All tests passed.")
Pow X N ▼ expand
"""
# 50. Pow(x, n)

Implement `pow(x, n)`, which calculates `x` raised to the power `n` (i.e., x^n).

## Examples

```text
Input: x = 2.00000, n = 10
Output: 1024.00000

Input: x = 2.10000, n = 3
Output: 9.26100

Input: x = 2.00000, n = -2
Output: 0.25000
Explanation: 2^-2 = 1/4 = 0.25
```

## Constraints
- -100.0 < x < 100.0
- -2^31 <= n <= 2^31 - 1
- n is an integer.
- Either x is not zero or n > 0.
- -10^4 <= x^n <= 10^4
"""


class Solution:
    def myPow_brute(self, x: float, n: int) -> float:
        # O(n) time — multiply x by itself n times (TLE for large n)
        if n < 0:
            x, n = 1 / x, -n
        result = 1.0
        for _ in range(n):
            result *= x
        return result

    def myPow(self, x: float, n: int) -> float:
        # Fast exponentiation: x^n = (x^(n//2))^2; halve n each step — O(log n)
        # Handle negative n by inverting x and using abs(n)
        # O(log n) time — fast exponentiation by squaring
        if n < 0:
            x, n = 1 / x, -n
        result = 1.0
        while n:
            if n % 2 == 1:
                result *= x
            x *= x
            n //= 2
        return result


if __name__ == '__main__':
    s = Solution()
    for method in (s.myPow_brute, s.myPow):
        assert abs(method(2.0, 10) - 1024.0) < 1e-5
        assert abs(method(2.0, -2) - 0.25) < 1e-5
        assert abs(method(2.0, 0) - 1.0) < 1e-5
    print("All tests passed.")
Roman To Integer ▼ expand
"""
# 13. Roman to Integer

Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M.

```text
Symbol  Value
I       1
V       5
X       10
L       50
C       100
D       500
M       1000
```

Roman numerals are usually written largest to smallest from left to right. However, when a smaller
value precedes a larger value, it is subtracted (e.g., IV = 4, IX = 9).

Given a roman numeral, convert it to an integer.

## Examples

```text
Input: s = "III"
Output: 3

Input: s = "LVIII"
Output: 58

Input: s = "MCMXCIV"
Output: 1994
```

## Constraints
- 1 <= s.length <= 15
- s contains only the characters ('I', 'V', 'X', 'L', 'C', 'D', 'M').
- It is guaranteed that s is a valid roman numeral in the range [1, 3999].
"""


class Solution:
    def romanToInt(self, s: str) -> int:
        # If current symbol < next symbol, subtract it (e.g. IV = 5-1)
        # Otherwise add it; process left to right
        # O(n) time — left-to-right scan, subtract when smaller value precedes larger
        val = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
        result = 0
        for i in range(len(s)):
            if i + 1 < len(s) and val[s[i]] < val[s[i+1]]:
                result -= val[s[i]]
            else:
                result += val[s[i]]
        return result


if __name__ == '__main__':
    s = Solution()
    assert s.romanToInt("III") == 3
    assert s.romanToInt("LVIII") == 58
    assert s.romanToInt("MCMXCIV") == 1994
    assert s.romanToInt("IV") == 4
    print("All tests passed.")
Rotate Image ▼ expand
"""
# 48. Rotate Image

You are given an `n x n` 2D matrix representing an image. Rotate the image by 90 degrees clockwise **in-place**.

You must modify the input matrix directly. Do not allocate another 2D matrix.

## Examples

```text
Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]
Output: [[7,4,1],[8,5,2],[9,6,3]]

Input: matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
Output: [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
```

## Constraints
- n == matrix.length == matrix[i].length
- 1 <= n <= 20
- -1000 <= matrix[i][j] <= 1000
"""

from typing import List


class Solution:
    def rotate_transpose(self, matrix: List[List[int]]) -> None:
        # O(n^2) time, O(1) space — transpose then reverse each row
        n = len(matrix)
        for i in range(n):
            for j in range(i + 1, n):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
        for row in matrix:
            row.reverse()

    def rotate(self, matrix: List[List[int]]) -> None:
        # Transpose the matrix (swap matrix[i][j] with matrix[j][i])
        # Then reverse each row to achieve 90-degree clockwise rotation
        # O(n^2) time, O(1) space — four-way cyclic swap layer by layer
        n = len(matrix)
        for i in range(n // 2):
            for j in range(i, n - 1 - i):
                tmp = matrix[i][j]
                matrix[i][j] = matrix[n-1-j][i]
                matrix[n-1-j][i] = matrix[n-1-i][n-1-j]
                matrix[n-1-i][n-1-j] = matrix[j][n-1-i]
                matrix[j][n-1-i] = tmp


if __name__ == '__main__':
    import copy
    s = Solution()
    for method in (s.rotate_transpose, s.rotate):
        m = [[1,2,3],[4,5,6],[7,8,9]]
        method(m)
        assert m == [[7,4,1],[8,5,2],[9,6,3]], f"Failed: {m}"
    print("All tests passed.")
Set Matrix Zeroes ▼ expand
"""
# 73. Set Matrix Zeroes

Given an `m x n` integer matrix, if an element is `0`, set its entire row and column to `0`s.

You must do it **in place**.

## Examples

```text
Input: matrix = [[1,1,1],[1,0,1],[1,1,1]]
Output: [[1,0,1],[0,0,0],[1,0,1]]

Input: matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
Output: [[0,0,0,0],[0,4,5,0],[0,3,1,0]]
```

## Constraints
- m == matrix.length
- n == matrix[0].length
- 1 <= m, n <= 200
- -2^31 <= matrix[i][j] <= 2^31 - 1
"""

from typing import List


class Solution:
    def setZeroes_extra_space(self, matrix: List[List[int]]) -> None:
        # O(m*n) time, O(m+n) space — record zero rows/cols then zero them out
        rows, cols = set(), set()
        for r in range(len(matrix)):
            for c in range(len(matrix[0])):
                if matrix[r][c] == 0:
                    rows.add(r); cols.add(c)
        for r in range(len(matrix)):
            for c in range(len(matrix[0])):
                if r in rows or c in cols:
                    matrix[r][c] = 0

    def setZeroes(self, matrix: List[List[int]]) -> None:
        # Use first row and first column as markers to avoid O(m+n) extra space
        # First pass: mark rows/cols that need zeroing using first row/col
        # Second pass: zero cells based on markers; then zero first row/col if needed
        # O(m*n) time, O(1) space — use first row/col as markers
        m, n = len(matrix), len(matrix[0])
        first_row_zero = any(matrix[0][c] == 0 for c in range(n))
        first_col_zero = any(matrix[r][0] == 0 for r in range(m))
        for r in range(1, m):
            for c in range(1, n):
                if matrix[r][c] == 0:
                    matrix[r][0] = matrix[0][c] = 0
        for r in range(1, m):
            for c in range(1, n):
                if matrix[r][0] == 0 or matrix[0][c] == 0:
                    matrix[r][c] = 0
        if first_row_zero:
            for c in range(n): matrix[0][c] = 0
        if first_col_zero:
            for r in range(m): matrix[r][0] = 0


if __name__ == '__main__':
    import copy
    s = Solution()
    for method in (s.setZeroes_extra_space, s.setZeroes):
        m = [[1,1,1],[1,0,1],[1,1,1]]
        method(m)
        assert m == [[1,0,1],[0,0,0],[1,0,1]], f"Failed: {m}"
    print("All tests passed.")
Spiral Matrix ▼ expand
"""
# 54. Spiral Matrix

Given an `m x n` matrix, return all elements of the matrix in spiral order.

## Examples

```text
Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]
Output: [1,2,3,6,9,8,7,4,5]

Input: matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]
```

## Constraints
- m == matrix.length
- n == matrix[i].length
- 1 <= m, n <= 10
- -100 <= matrix[i][j] <= 100
"""

from typing import List


class Solution:
    def spiralOrder_layers(self, matrix: List[List[int]]) -> List[int]:
        # O(m*n) time — peel off outer layer iteratively
        result = []
        top, bottom, left, right = 0, len(matrix) - 1, 0, len(matrix[0]) - 1
        while top <= bottom and left <= right:
            result += matrix[top][left:right+1]
            for r in range(top + 1, bottom + 1):
                result.append(matrix[r][right])
            if top < bottom:
                result += matrix[bottom][left:right][::-1]
            if left < right:
                for r in range(bottom - 1, top, -1):
                    result.append(matrix[r][left])
            top += 1; bottom -= 1; left += 1; right -= 1
        return result

    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        # Shrink boundaries (top, bottom, left, right) after traversing each side
        # Traverse: left-to-right, top-to-bottom, right-to-left, bottom-to-top
        # O(m*n) time — direction simulation with visited marking
        m, n = len(matrix), len(matrix[0])
        dirs = [(0,1),(1,0),(0,-1),(-1,0)]
        d = r = c = 0
        result = []
        for _ in range(m * n):
            result.append(matrix[r][c])
            matrix[r][c] = None
            nr, nc = r + dirs[d][0], c + dirs[d][1]
            if not (0 <= nr < m and 0 <= nc < n and matrix[nr][nc] is not None):
                d = (d + 1) % 4
                nr, nc = r + dirs[d][0], c + dirs[d][1]
            r, c = nr, nc
        return result


if __name__ == '__main__':
    s = Solution()
    assert s.spiralOrder_layers([[1,2,3],[4,5,6],[7,8,9]]) == [1,2,3,6,9,8,7,4,5]
    assert s.spiralOrder([[1,2,3],[4,5,6],[7,8,9]]) == [1,2,3,6,9,8,7,4,5]
    assert s.spiralOrder_layers([[1,2,3,4],[5,6,7,8],[9,10,11,12]]) == [1,2,3,4,8,12,11,10,9,5,6,7]
    print("All tests passed.")
Transpose Matrix ▼ expand
"""
# 867. Transpose Matrix

Given a 2D integer array `matrix`, return the transpose of `matrix`.

The transpose of a matrix is the matrix flipped over its main diagonal,
switching the matrix's row and column indices.

## Examples

```text
Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]
Output: [[1,4,7],[2,5,8],[3,6,9]]

Input: matrix = [[1,2,3],[4,5,6]]
Output: [[1,4],[2,5],[3,6]]
```

## Constraints
- m == matrix.length
- n == matrix[i].length
- 1 <= m, n <= 1000
- 1 <= m * n <= 10^5
- -10^9 <= matrix[i][j] <= 10^9
"""

from typing import List


class Solution:
    def transpose(self, matrix: List[List[int]]) -> List[List[int]]:
        # Swap rows and columns: result[j][i] = matrix[i][j]
        # O(m*n) time, O(m*n) space — build transposed matrix directly
        m, n = len(matrix), len(matrix[0])
        return [[matrix[r][c] for r in range(m)] for c in range(n)]


if __name__ == '__main__':
    s = Solution()
    assert s.transpose([[1,2,3],[4,5,6],[7,8,9]]) == [[1,4,7],[2,5,8],[3,6,9]]
    assert s.transpose([[1,2,3],[4,5,6]]) == [[1,4],[2,5],[3,6]]
    print("All tests passed.")

Spiral Matrix

Traverse an n×n matrix in spiral order using boundary shrinking