LeetCode: Math and Geometry

Math and Geometry Intro
What is Math and Geometry
math!
Math and Geometry IRL
math!
Math and Geometry Application: Math and Geometry
Pattern: math!
Ex: bits numbers!!
def math!(n: int) -> int:
return n+1202. Happy Number ::1:: - Easy
Topics: Hash Table, Math, Two Pointers
Intro
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 which does not include 1. Those numbers for which this process ends in 1 are happy. Return true if n is a happy number, and false if not.
| Example Input | Output |
|---|---|
| n = 19 | true |
| n = 2 | false |
Constraints:
1 ≤ n ≤ 231 - 1
Abstraction
Given a number and constraints, determine if number fits constraints.
Pseudocode
text will go here
Solution 1: Hash Set Detection - Math and Geometry/Math and Geometry
def isHappy(self, n: int) -> bool:
# Note:
# 1. Compute sum of squares of digits repeatedly
# 2. Keep track of numbers seen in a hash set
# 3. If we see a number again, a cycle exists → not happy
# 4. If we reach 1, number is happy
# Result -> Determine if number is happy
# store numbers to detect cycles
seen = set()
# cycle formula
def next_number(num):
return sum(int(d) ** 2 for d in str(num))
while n != 1:
# cycle detected
if n in seen:
return False
seen.add(n)
# compute sum of squares of digits
n = next_number(n)
# reached 1 = happy number
res = True
# overall: time complexity O(log n) (per iteration * number of unique sums)
# overall: space complexity O(log n) (for hash set)
return resSolution 2: Slow Fast Pointer - Math and Geometry/Math and Geometry
def isHappy(self, n: int) -> bool:
# Note:
# Treat sum-of-squares transformation as a linked list
# 2. Slow and fast pointers to detect cycle
# 3. If fast or fast.next reaches 1 -> happy
# 4. If slow == fast → cycle exists -> not happy
# Result -> Determine if number is happy
# cycle formula
def next_number(num):
return sum(int(d) ** 2 for d in str(num))
# set pointers
slow = n
fast = next_number(n)
# slow and fast pointer to check happy number requirements
while fast != 1 and slow != fast:
slow = next_number(slow)
fast = next_number(next_number(fast))
# happy number == 1
res = fast == 1
# overall: time complexity O(log n) (per iteration * number of iterations before cycle)
# overall: space complexity O(1)
return res66. Plus One ::1:: - Easy
Topics: Array, Math
Intro
You are given a large integer represented as an integer array digits, where each digits[i] is the ith digit of the integer. The digits are ordered from most significant to least significant in left-to-right order. The large integer does not contain any leading 0's. Increment the large integer by one and return the resulting array of digits.
| Example Input | Output |
|---|---|
| digits = [1,2,3] | [1,2,4] |
| digits = [4,3,2,1] | [4,3,2,2] |
| digits = [9] | [1,0] |
Constraints:
1 ≤ digits.length ≤ 100
0 ≤ digits[i] ≤ 9
digits does not contain any leading 0's.
Abstraction
Given a number represented as an array of digits (most significant digit first), increment the number by 1 and return the resulting array of digits.
Pseudocode
text will go here
Solution 1: Hash Set Detection - Math and Geometry/Math and Geometry
def plusOne(self, digits: List[int]) -> List[int]:
# Note:
# Process digits from least significant to most significant
# 1. Iterate right to left in array
# 2. Add 1 to the last digit
# 3. Propagate carry if sum >= 10
# 4. If carry remains after the most significant digit, insert at front
# 5. Time complexity O(n), space O(1) extra (besides output)
n = len(digits)
# iterate from last digit to first
for i in range(n - 1, -1, -1):
# add to current digit
digits[i] += 1
# if no carry, finished array, else continue
if digits[i] < 10:
return digits
# if carry, set current to zero, continue
digits[i] = 0
# if carry remains after processing all digits
# prepend [1] to array
res = [1] + digits
# overall: time complexity O(n)
# overall: space complexity O(1)
return res50. Pow(x, n) ::1:: - Medium
Topics: Math, Recursion
Intro
Implement pow(x, n), which calculates x raised to the power n (i.e., xn).
| Example Input | Output |
|---|---|
| x = 2.00000, n = 10 | 1024.00000 |
| x = 2.10000, n = 3 | 9.26100 |
| x = 2.00000, n = -2 | 0.25000 |
Constraints:
-100.0 < x < 100.0
-231 ≤ n ≤ 231 - 1
n is an integer.
Either x is not zero or n > 0.
-104 ≤ xn ≤ 104
Abstraction
Implement pow(x, n).
Pseudocode
text will go here
Solution 1: Recursive Fast Exponentiation - Math and Geometry/Math and Geometry
def myPow(self, x: float, n: int) -> float:
# Note:
# Math behind Fast Exponentiation:
# x^n -> even -> [x^(n/2)]^2
# -> odd -> x * (x^[(n-1)/2])^2
# Example:
# x^6 = (x^3)^2
# x^7 = x * (x^3)^2
# Now simply apply some recursion
# Note:
# 1. Recursively compute half power to reduce computation
# 2. If exponent is negative, use reciprocal: x^-n = 1 / x^n
# 3. Base case: n == 0 → return 1
# Result -> pow() calculation
if n == 0:
return 1.0
if n < 0:
return 1 / self.myPow(x, -n)
half = self.myPow(x, n // 2)
if n % 2 == 0:
return half * half
else:
return half * half * x
# overall: time complexity O(log n)
# overall: space complexity O(log n) due to recursion stackSolution 2: Iterative Fast Binary Exponentiation - Math and Geometry/Math and Geometry
def myPow(self, x: float, n: int) -> float:
# Note:
# Binary Exponentiation uses the binary representation of n
# n = 13 -> 1101
# Start with result = 1 and current_product = x.
# Then for each bit from LSB to MSB:
# If bit is 1 → multiply result by current_product
# Square current_product each step
# Shift to next bit
# Note:
# 1. Use binary exponentiation iteratively
# 2. Convert negative exponent to positive and invert at end
# 3. Multiply result by x whenever the current bit of n is 1
# 4. Shift exponent right each iteration
N = n
if N < 0:
x = 1 / x
N = -N
result = 1.0
current_product = x
while N > 0:
if N % 2 == 1: # current bit is 1
result *= current_product
current_product *= current_product # square for next bit
N //= 2
# overall: time complexity O(log n)
# overall: space complexity O(1)
return result43. Multiply Strings ::2:: - Medium
Topics: Math, Recursion
Intro
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.
| Example Input | Output |
|---|---|
| num1 = "2", num2 = "3" | "6" |
| num1 = "123", num2 = "456" | "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.
Abstraction
Given two numbers represented as strings, return the product of the two numbers.
Pseudocode
text will go here
Solution 1: Simulate Grade School Multiplication - Math and Geometry/Math and Geometry
def multiply(self, num1: str, num2: str) -> str:
# Note:
# 1. Multiply each digit of num1 by each digit of num2
# 2. Store intermediate sums in an array of length len(num1) + len(num2)
# 3. Handle carry for each position
# 4. Convert array to string, skipping leading zeros
# Zero Check
if num1 == "0" or num2 == "0":
return "0"
m, n = len(num1), len(num2)
# max number of digits the product can have when multiplying two integers
pos = [0] * (m + n)
# multiply digits from right to left
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
# i -> index of digit in num1
# j -> index of digit in num2
# single digit multiplication
mul = int(num1[i]) * int(num2[j])
# stores the carry that will affect the next higher digit
p1 = i + j
# stores the ones place for this multiplication
p2 = i + j + 1
# pos[p2] holds previous carry that contributes to new multiplication
total = mul + pos[p2]
# new current digit
pos[p2] = total % 10
# new carry
pos[p1] += total // 10
# convert to string, skipping leading zeros
result = []
# to skip leading zeros in final result
for p in pos:
# if result == empty, ensures we haven't added any non-zero digits yet
# which ensures we only skip leading zeros, not inner zeros
if not result and p == 0:
continue
# append inner zeros or values
result.append(str(p))
# more efficient of concatenating strings
res = "".join(result)
# overall: time complexity O(m*n)
# overall: space complexity O(m+n)
return res2013. Detect Squares ::1:: - Medium
Topics: Array, Hash Table, Design, Counting
Intro
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. Duplicate points are allowed and should be treated as different points. Given a query point, counts the number of ways such that the three points and the query point form an axis-aligned square with positive area. An axis-aligned square is a square whose edges are all the same length and are either parallel or perpendicular to the x-axis and y-axis. 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 point = [x, y] as described above.
| Example Input | Output |
|---|---|
| too long | [null, null, null, null, 1, 0, null, 2] |
Constraints:
point.length == 2
0 ≤ x, y, ≤ 1000
At most 3000 calls in total will be made to add and count.
Abstraction
Design a data structure to store points on a 2D grid and count the number of axis-aligned squares that can be formed with a given query point. Points can be added multiple times. Squares must have edges parallel to axes and positive area. Efficiently handle up to 3000 add and count calls.
Pseudocode
text will go here
Solution 1: Hash Map Counting - Math and Geometry/Math and Geometry
class DetectSquares:
def __init__(self):
# Note:
# counts[x][y] -> tracks how many times point (x,y) has been added
# This allows O(1) insertion and O(k) count queries
self.counts = defaultdict(lambda: defaultdict(int))
def add(self, point: List[int]) -> None:
# Increment point count
x_col, y_row = point
self.counts[x_col][y_row] += 1
def count(self, point: List[int]) -> int:
x_col, y_row = point
total = 0
# Look for all points in same column x, but with different y
for curr_y_row, count_at_point in self.counts[x_col].items():
if curr_y_row == y_row: # skip the query point itself
continue
# distance = side length
d = curr_y_row - y_row
# Left square
left_x = x_col - d
count_left_top = self.counts.get(left_x, {}).get(y_row, 0)
count_left_bottom = self.counts.get(left_x, {}).get(curr_y_row, 0)
total += count_at_point * count_left_top * count_left_bottom
# Right square
right_x = x_col + d
count_right_top = self.counts.get(right_x, {}).get(y_row, 0)
count_right_bottom = self.counts.get(right_x, {}).get(curr_y_row, 0)
total += count_at_point * count_right_top * count_right_bottom
return total
# overall: add O(1), count O(k) where k = # points sharing same x
# overall: space complexity O(n) for n points stored