You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
5.7 KiB

import copy
import typing
import stdlib
def yep(s: str) -> bool:
return s.strip().lower().startswith("y")
def guess_bisect_repl(lower: int, upper: int) -> int:
mid = lower + ((upper - lower) // 2)
if yep(input(f"is it {mid}? ")):
return mid
if yep(input(f"higher than {mid}? ")):
return guess_bisect_repl(mid, upper)
return guess_bisect_repl(lower, mid)
def find_sqrt_ish(n: int) -> int:
return int(find_bisect(0, n, gen_sqrt_check(n)))
def gen_sqrt_check(n: int) -> typing.Callable[[float], int]:
def check(mid: float) -> int:
mid_sq: float = mid * mid
if mid_sq == n:
return 0
if mid_sq < n:
return 1
return -1
return check
def find_bisect(
lower: float, upper: float, check: typing.Callable[[float], int]
) -> float:
mid: float = lower + ((upper - lower) / 2)
print(f"lower={lower} mid={mid} upper={upper}")
if mid == lower or mid == upper or check(mid) == 0:
return mid
if check(mid) == 1:
return find_bisect(mid, upper, check)
return find_bisect(lower, mid, check)
def cartesian_path(p0: tuple[int, int], p1: tuple[int, int]) -> list[tuple[int, int]]:
path: list[tuple[int, int]] = []
if p0 < p1:
for i in range(p0[1], p1[1]):
path.append((i, p0[0]))
for i in range(p0[0], p1[0]):
path.append((p1[1], i))
else:
for i in range(p0[1], p1[1] - 1, -1):
path.append((i, p0[0]))
for i in range(p0[0] - 1, p1[0], -1):
path.append((p1[1], i))
return path
def gen_matrix(width: int, height: int) -> list[list[int]]:
return [list(range(width)) for _ in range(height)]
class Roman:
SIMPLE = {
"I": 1,
"V": 5,
"X": 10,
"L": 50,
"C": 100,
"D": 500,
"M": 1000,
}
SIMPLE_REVERSE = {v: k for k, v in SIMPLE.items()}
COMPOUND = {
"IV": 4,
"IX": 9,
"XL": 40,
"XC": 90,
"CD": 400,
"CM": 900,
}
PREFIXES = {k[0] for k in COMPOUND.keys()}
COMPOUND_REVERSE = {v: k for k, v in COMPOUND.items()}
ALL = SIMPLE | COMPOUND
ALL_REVERSE = {v: k for k, v in ALL.items()}
@classmethod
def i2r(cls, i: int) -> str:
if i > 100_000:
raise ValueError(f"{i} is too silly")
r: list[str] = []
for int_val, roman_val in sorted(cls.ALL_REVERSE.items(), reverse=True):
remainder = i % int_val
r += [roman_val] * int((i - remainder) / int_val)
i = remainder
return "".join(r)
@classmethod
def r2i(cls, r: str) -> int:
total = 0
offset = 0
for i in range(len(r)):
if i + offset > len(r) - 1:
break
c = r[i + offset]
if (
c in cls.PREFIXES
and (i + offset + 1) < len(r)
and c + r[i + offset + 1] in cls.ALL
):
total += cls.ALL[c + r[i + offset + 1]]
offset += 1
continue
total += cls.ALL[c]
return total
class MinStack:
def __init__(self):
self._v: list[int] = []
self._min: list[int] = []
def push(self, val: int) -> None:
self._v.append(val)
self._min.append(min(val, self._min[-1] if self._min else val))
def pop(self) -> None:
self._v.pop(-1)
self._min.pop(-1)
def top(self) -> int:
return self._v[-1]
def getMin(self) -> int: # no qa
return self._min[-1]
def linked_list_to_list(head: stdlib.ListNode | None) -> list[int]:
seen: set[int] = set()
ret: list[int] = []
while head is not None:
if hash(head) in seen:
return ret
seen.add(hash(head))
ret.append(head.val)
head = head.next
return ret
def sort_linked_list(head: stdlib.ListNode | None) -> stdlib.ListNode | None:
by_val: list[tuple[int, stdlib.ListNode]] = []
ret: stdlib.ListNode | None = None
while head is not None:
by_val.append((head.val, head))
head = head.next
cur = ret
for _, node in sorted(by_val, key=lambda v: v[0]):
if cur is None:
cur = ret = node
continue
cur.next = node
cur = cur.next
if cur is not None:
cur.next = None
return ret
def connect_binary_tree_right(
root: stdlib.Node | None,
) -> tuple[stdlib.Node | None, list[int | None]]:
if root is None:
return None, []
by_level = binary_tree_by_level(copy.deepcopy(root))
serialized: list[int | None] = []
print("")
if 0 not in by_level or len(by_level[0]) == 0:
return None, []
connected_root = by_level[0][0]
for level, nodes in sorted(by_level.items(), key=lambda p: p[0]):
for i in range(len(nodes)):
serialized.append(nodes[i].val)
if len(nodes) > i + 1:
print(f"{'-' * level}> connecting {nodes[i].val} -> {nodes[i + 1].val}")
nodes[i].next = nodes[i + 1]
serialized.append(None)
return connected_root, serialized
def binary_tree_by_level(root: stdlib.Node) -> dict[int, list[stdlib.Node]]:
combined: dict[int, list[stdlib.Node]] = {}
for path in collect_binary_tree_levels(0, root):
level, node = path
combined.setdefault(level, [])
combined[level].insert(0, node)
return combined
def collect_binary_tree_levels(
level: int, node: stdlib.Node | None
) -> typing.Iterator[tuple[int, stdlib.Node]]:
if node is None:
return
yield (level, node)
yield from collect_binary_tree_levels(level + 1, node.right)
yield from collect_binary_tree_levels(level + 1, node.left)