subtree(users/wpcarro): docking briefcase at '24f5a642'

git-subtree-dir: users/wpcarro
git-subtree-mainline: 464bbcb15c
git-subtree-split: 24f5a642af
Change-Id: I6105b3762b79126b3488359c95978cadb3efa789
This commit is contained in:
Vincent Ambo 2021-12-14 01:51:19 +03:00
commit 019f8fd211
766 changed files with 175420 additions and 0 deletions

View file

@ -0,0 +1,6 @@
# Scratch
The purpose of the `scratch` directory is to host practice exercises. Practice
encompasses things like working on data structures and algorithms problems for
upcoming coding interviews or general aptitude as well as writing code snippets
to help me learn a new programming language or understand an unfamiliar concept.

View file

@ -0,0 +1,4 @@
# 2019 Advent of Code
Here are my attempts at the 2019 Advent of Code challenge before my dedication
to the effort plummeted.

View file

@ -0,0 +1,119 @@
from math import floor
xs = [
102473,
84495,
98490,
68860,
62204,
72810,
65185,
145951,
77892,
108861,
70764,
67286,
74002,
80773,
52442,
131505,
107162,
126993,
59784,
64231,
91564,
68585,
98735,
69020,
77332,
60445,
65826,
111506,
95431,
146687,
135119,
86804,
95915,
85434,
111303,
148127,
132921,
136213,
89004,
143137,
144853,
143017,
104386,
100612,
54760,
63813,
144191,
84481,
69718,
84936,
98621,
124993,
92736,
60369,
137284,
101902,
112726,
51784,
126496,
85005,
101661,
137278,
136637,
90340,
100209,
53683,
50222,
132060,
98797,
139054,
135638,
100632,
137849,
125333,
103981,
76954,
134352,
74229,
93402,
62552,
50286,
57066,
98439,
120708,
117827,
107884,
72837,
148663,
125645,
61460,
120555,
142473,
106668,
58612,
58576,
143366,
90058,
121087,
89546,
126161,
]
def fuel_for_mass(x):
"""Return the amount of fuel (in mass) required for a mass of X. The total
amount of fuel includes the amount of fuel required for the fuel itself,
since fuel also has a mass weights."""
mass_fuel = floor(x / 3) - 2
if mass_fuel < 0:
return 0
else:
fuel_fuel = fuel_for_mass(mass_fuel)
return mass_fuel + fuel_fuel
print(sum(fuel_for_mass(x) for x in xs))

View file

@ -0,0 +1,32 @@
from itertools import product
x = [
1, 0, 0, 3, 1, 1, 2, 3, 1, 3, 4, 3, 1, 5, 0, 3, 2, 1, 10, 19, 1, 6, 19, 23,
2, 23, 6, 27, 2, 6, 27, 31, 2, 13, 31, 35, 1, 10, 35, 39, 2, 39, 13, 43, 1,
43, 13, 47, 1, 6, 47, 51, 1, 10, 51, 55, 2, 55, 6, 59, 1, 5, 59, 63, 2, 9,
63, 67, 1, 6, 67, 71, 2, 9, 71, 75, 1, 6, 75, 79, 2, 79, 13, 83, 1, 83, 10,
87, 1, 13, 87, 91, 1, 91, 10, 95, 2, 9, 95, 99, 1, 5, 99, 103, 2, 10, 103,
107, 1, 107, 2, 111, 1, 111, 5, 0, 99, 2, 14, 0, 0
]
def interpret(i, x):
op, a, b, out = x[i + 0], x[i + 1], x[i + 2], x[i + 3]
if op == 1:
x[out] = x[a] + x[b]
return interpret(i + 4, x)
elif op == 2:
x[out] = x[a] * x[b]
return interpret(i + 4, x)
elif op == 99:
return x
else:
raise Exception('Unsupported opcode: {}. {}, {}'.format(op, a, b))
for a, b in product(range(100), range(100)):
y = x[:]
y[1] = a
y[2] = b
if interpret(0, y)[0] == 19690720:
print(100 * a + b)

View file

@ -0,0 +1,137 @@
from math import floor
from heapq import heappush, heappop
xs = [
"R1009", "U993", "L383", "D725", "R163", "D312", "R339", "U650", "R558",
"U384", "R329", "D61", "L172", "D555", "R160", "D972", "L550", "D801",
"L965", "U818", "L123", "D530", "R176", "D353", "L25", "U694", "L339",
"U600", "L681", "D37", "R149", "D742", "R762", "U869", "R826", "U300",
"L949", "U978", "L303", "U361", "R136", "D343", "L909", "U551", "R745",
"U913", "L566", "D292", "R820", "U886", "R205", "D431", "L93", "D71",
"R577", "U872", "L705", "U510", "L698", "U963", "R607", "U527", "L669",
"D543", "R690", "U954", "L929", "D218", "R490", "U500", "L589", "D332",
"R949", "D538", "R696", "U659", "L188", "U468", "L939", "U833", "L445",
"D430", "R78", "D303", "R130", "D649", "R849", "D712", "L511", "U745",
"R51", "U973", "R799", "U829", "R605", "D771", "L837", "U204", "L414",
"D427", "R538", "U116", "R540", "D168", "R493", "U900", "L679", "U431",
"L521", "D500", "L428", "U332", "L954", "U717", "L853", "D339", "L88",
"U807", "L607", "D496", "L163", "U468", "L25", "U267", "L759", "D898",
"L591", "U445", "L469", "U531", "R596", "D486", "L728", "D677", "R350",
"D429", "R39", "U568", "R92", "D875", "L835", "D841", "R877", "U178",
"L221", "U88", "R592", "U692", "R455", "U693", "L419", "U90", "R609",
"U672", "L293", "U168", "R175", "D456", "R319", "D570", "R504", "D165",
"L232", "D624", "L604", "D68", "R807", "D59", "R320", "D281", "L371",
"U956", "L788", "D897", "L231", "D829", "R287", "D798", "L443", "U194",
"R513", "D925", "L232", "U225", "L919", "U563", "R448", "D889", "R661",
"U852", "L950", "D558", "L269", "U186", "L625", "U673", "L995", "U732",
"R435", "U849", "L413", "D690", "L158", "D234", "R361", "D458", "L271",
"U90", "L781", "U754", "R256", "U162", "L842", "U927", "L144", "D62",
"R928", "D238", "R473", "U97", "L745", "U303", "L487", "D349", "L520",
"D31", "L825", "U385", "L133", "D948", "L39", "U62", "R801", "D664",
"L333", "U134", "R692", "U385", "L658", "U202", "L279", "D374", "R489",
"D686", "L182", "U222", "R733", "U177", "R94", "D603", "L376", "U901",
"R216", "D851", "L155", "D214", "L460", "U758", "R121", "D746", "L180",
"U175", "L943", "U146", "L166", "D251", "L238", "U168", "L642", "D341",
"R281", "U182", "R539", "D416", "R553", "D67", "L748", "U272", "R257",
"D869", "L340", "U180", "R791", "U138", "L755", "D976", "R731", "U713",
"R602", "D284", "L258", "U176", "R509", "U46", "R935", "U576", "R96",
"U89", "L913", "U703", "R833"
]
ys = [
"L1006", "D998", "R94", "D841", "R911", "D381", "R532", "U836", "L299",
"U237", "R781", "D597", "L399", "D800", "L775", "D405", "L485", "U636",
"R589", "D942", "L878", "D779", "L751", "U711", "L973", "U410", "L151",
"U15", "L685", "U417", "L106", "D648", "L105", "D461", "R448", "D743",
"L589", "D430", "R883", "U37", "R155", "U350", "L421", "U23", "R337",
"U816", "R384", "D671", "R615", "D410", "L910", "U914", "L579", "U385",
"R916", "U13", "R268", "D519", "R289", "U410", "L389", "D885", "L894",
"U734", "L474", "U707", "L72", "U155", "L237", "U760", "L127", "U806",
"L15", "U381", "L557", "D727", "L569", "U320", "L985", "D452", "L8",
"D884", "R356", "U732", "L672", "D458", "L485", "U402", "L238", "D30",
"R644", "U125", "R753", "U183", "L773", "U487", "R849", "U210", "L164",
"D808", "L595", "D668", "L340", "U785", "R313", "D72", "L76", "D263",
"R689", "U604", "R471", "U688", "R462", "D915", "R106", "D335", "R869",
"U499", "R190", "D916", "R468", "D882", "R56", "D858", "L143", "D741",
"L386", "U856", "R50", "U853", "R151", "D114", "L773", "U854", "L290",
"D344", "L23", "U796", "L531", "D932", "R314", "U960", "R643", "D303",
"L661", "D493", "L82", "D491", "L722", "U848", "L686", "U4", "L985",
"D509", "L135", "D452", "R500", "U105", "L326", "D101", "R222", "D944",
"L645", "D362", "L628", "U305", "L965", "U356", "L358", "D137", "R787",
"U728", "R967", "U404", "R18", "D928", "L695", "D965", "R281", "D597",
"L791", "U731", "R746", "U163", "L780", "U41", "L255", "U81", "L530",
"D964", "R921", "D297", "R475", "U663", "L226", "U623", "L984", "U943",
"L143", "U201", "R926", "U572", "R343", "U839", "R764", "U751", "R128",
"U939", "R987", "D108", "R474", "U599", "R412", "D248", "R125", "U797",
"L91", "D761", "L840", "U290", "L281", "U779", "R650", "D797", "R185",
"D320", "L25", "U378", "L696", "U332", "R75", "D620", "L213", "D667",
"R558", "U267", "L846", "U306", "R939", "D220", "R311", "U827", "R345",
"U534", "R56", "D679", "R48", "D845", "R898", "U8", "R862", "D960", "R753",
"U319", "L886", "D795", "R805", "D265", "R876", "U729", "R894", "D368",
"R858", "U744", "R506", "D327", "L903", "U919", "L721", "U507", "L463",
"U753", "R775", "D719", "R315", "U128", "R17", "D376", "R999", "D386",
"L259", "U181", "L162", "U605", "L265", "D430", "R35", "D968", "R207",
"U466", "R796", "D667", "R93", "U749", "L315", "D410", "R312", "U929",
"L923", "U260", "R638"
]
def to_coords(xs):
row, col = 0, 0
coords = []
for x in xs:
d, amt = x[0], int(x[1:])
if d == 'U':
for i in range(1, amt + 1):
coords.append((row + i, col))
row += amt
elif d == 'D':
for i in range(1, amt + 1):
coords.append((row - i, col))
row -= amt
elif d == 'L':
for i in range(1, amt + 1):
coords.append((row, col - i))
col -= amt
elif d == 'R':
for i in range(1, amt + 1):
coords.append((row, col + i))
col += i
return coords
def contains(row, col, d):
if row not in d:
return False
return col in d[row]
def intersections(xs, ys):
d = {}
ints = set()
for row, col in to_coords(xs):
if row in d:
d[row].add(col)
else:
d[row] = {col}
for row, col in to_coords(ys):
if contains(row, col, d):
ints.add((row, col))
return ints
def trace_to(coord, xs):
count = 0
for coord_x in to_coords(xs):
count += 1
if coord_x == coord:
return count
raise Exception("Intersection doesn't exist")
answer = []
for coord in intersections(xs, ys):
x = trace_to(coord, xs)
y = trace_to(coord, ys)
heappush(answer, x + y)
print(heappop(answer))

View file

@ -0,0 +1,35 @@
import re
start = 134792
end = 675810
def satisfies(x):
x = str(x)
result = False
double, not_decreasing = False, False
# double and *only* double exists
for i in range(len(x) - 1):
# double and left-of-a is BOL or !x
# and right-of-b is EOL or !x
a, b = x[i], x[i + 1]
bol = i - 1 < 0
eol = i + 2 >= len(x)
if a == b and (bol or x[i - 1] != a) and (eol or x[i + 2] != a):
double = True
break
# not_decreasing
prev = int(x[0])
for a in x[1:]:
a = int(a)
if prev > a:
return False
prev = a
not_decreasing = True
return double and not_decreasing
print(len([x for x in range(start, end + 1) if satisfies(x)]))

View file

@ -0,0 +1,170 @@
x = [
3, 225, 1, 225, 6, 6, 1100, 1, 238, 225, 104, 0, 1102, 31, 68, 225, 1001,
13, 87, 224, 1001, 224, -118, 224, 4, 224, 102, 8, 223, 223, 1001, 224, 7,
224, 1, 223, 224, 223, 1, 174, 110, 224, 1001, 224, -46, 224, 4, 224, 102,
8, 223, 223, 101, 2, 224, 224, 1, 223, 224, 223, 1101, 13, 60, 224, 101,
-73, 224, 224, 4, 224, 102, 8, 223, 223, 101, 6, 224, 224, 1, 224, 223,
223, 1101, 87, 72, 225, 101, 47, 84, 224, 101, -119, 224, 224, 4, 224,
1002, 223, 8, 223, 1001, 224, 6, 224, 1, 223, 224, 223, 1101, 76, 31, 225,
1102, 60, 43, 225, 1102, 45, 31, 225, 1102, 63, 9, 225, 2, 170, 122, 224,
1001, 224, -486, 224, 4, 224, 102, 8, 223, 223, 101, 2, 224, 224, 1, 223,
224, 223, 1102, 29, 17, 224, 101, -493, 224, 224, 4, 224, 102, 8, 223, 223,
101, 1, 224, 224, 1, 223, 224, 223, 1102, 52, 54, 225, 1102, 27, 15, 225,
102, 26, 113, 224, 1001, 224, -1560, 224, 4, 224, 102, 8, 223, 223, 101, 7,
224, 224, 1, 223, 224, 223, 1002, 117, 81, 224, 101, -3645, 224, 224, 4,
224, 1002, 223, 8, 223, 101, 6, 224, 224, 1, 223, 224, 223, 4, 223, 99, 0,
0, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1105, 0, 99999, 1105, 227, 247,
1105, 1, 99999, 1005, 227, 99999, 1005, 0, 256, 1105, 1, 99999, 1106, 227,
99999, 1106, 0, 265, 1105, 1, 99999, 1006, 0, 99999, 1006, 227, 274, 1105,
1, 99999, 1105, 1, 280, 1105, 1, 99999, 1, 225, 225, 225, 1101, 294, 0, 0,
105, 1, 0, 1105, 1, 99999, 1106, 0, 300, 1105, 1, 99999, 1, 225, 225, 225,
1101, 314, 0, 0, 106, 0, 0, 1105, 1, 99999, 8, 226, 677, 224, 102, 2, 223,
223, 1005, 224, 329, 1001, 223, 1, 223, 1108, 677, 226, 224, 102, 2, 223,
223, 1006, 224, 344, 101, 1, 223, 223, 108, 677, 226, 224, 102, 2, 223,
223, 1006, 224, 359, 101, 1, 223, 223, 7, 677, 226, 224, 102, 2, 223, 223,
1005, 224, 374, 101, 1, 223, 223, 1007, 226, 677, 224, 102, 2, 223, 223,
1005, 224, 389, 101, 1, 223, 223, 8, 677, 677, 224, 102, 2, 223, 223, 1006,
224, 404, 1001, 223, 1, 223, 1007, 677, 677, 224, 1002, 223, 2, 223, 1006,
224, 419, 101, 1, 223, 223, 1108, 677, 677, 224, 1002, 223, 2, 223, 1005,
224, 434, 1001, 223, 1, 223, 1107, 226, 677, 224, 102, 2, 223, 223, 1005,
224, 449, 101, 1, 223, 223, 107, 226, 226, 224, 102, 2, 223, 223, 1006,
224, 464, 101, 1, 223, 223, 1108, 226, 677, 224, 1002, 223, 2, 223, 1005,
224, 479, 1001, 223, 1, 223, 7, 677, 677, 224, 102, 2, 223, 223, 1006, 224,
494, 1001, 223, 1, 223, 1107, 677, 226, 224, 102, 2, 223, 223, 1005, 224,
509, 101, 1, 223, 223, 107, 677, 677, 224, 1002, 223, 2, 223, 1006, 224,
524, 101, 1, 223, 223, 1008, 677, 677, 224, 1002, 223, 2, 223, 1006, 224,
539, 101, 1, 223, 223, 7, 226, 677, 224, 1002, 223, 2, 223, 1005, 224, 554,
101, 1, 223, 223, 108, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 569,
101, 1, 223, 223, 1008, 226, 677, 224, 102, 2, 223, 223, 1005, 224, 584,
101, 1, 223, 223, 8, 677, 226, 224, 1002, 223, 2, 223, 1005, 224, 599, 101,
1, 223, 223, 1007, 226, 226, 224, 1002, 223, 2, 223, 1005, 224, 614, 101,
1, 223, 223, 1107, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 629, 101,
1, 223, 223, 107, 677, 226, 224, 1002, 223, 2, 223, 1005, 224, 644, 1001,
223, 1, 223, 1008, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 659, 101,
1, 223, 223, 108, 677, 677, 224, 1002, 223, 2, 223, 1005, 224, 674, 1001,
223, 1, 223, 4, 223, 99, 226
]
# Interpretter spec:
# Op-code width: 2
# ABCDE
# A: Mode of 3rd parameter
# B: Mode of 2rd parameter
# C: Mode of 1st parameter
# DE: 2-digit op-code
#
# Not every op-code has the same arity.
#
# Parameter modes:
# - positional: index of memory. 0
# - immediate: raw value. 1
# Assert that you never attempt to write to an "immediate value"
# Parameter modes
POS = '0' # positional parameter mode
VAL = '1' # immediate parameter mode
# Pasted from day-2.py
# interpretter :: Int -> [Int] -> [Int] -> IO ()
def interpret(i, x, argv=[], outs=[]):
"""Values in `argv` will be applied to any `input` fields."""
# The widest op-code we'll see is 3 + 2 = 5 for either addition or
# multiplication since each of those is a 3-arity function with a two-digit
# op-code.
instruction = '{:05d}'.format(x[i])
op = instruction[-2:]
if op == '01':
a, b, out = x[i + 1], x[i + 2], x[i + 3]
mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[
0]
a = a if mode_a == VAL else x[a]
b = b if mode_b == VAL else x[b]
assert mode_out == POS
x[out] = a + b
return interpret(i + 4, x, argv=argv, outs=outs)
elif op == '02':
a, b, out = x[i + 1], x[i + 2], x[i + 3]
mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[
0]
a = a if mode_a == VAL else x[a]
b = b if mode_b == VAL else x[b]
assert mode_out == POS
x[out] = a * b
return interpret(i + 4, x, argv=argv, outs=outs)
# input
elif op == '03':
a = x[i + 1]
mode_a = instruction[2]
assert mode_a == POS
# What's the pythonic way to defensively get this value?
if len(argv) and argv[0] is not None:
x[a] = argv[0]
return interpret(i + 2, x, argv=argv[1:], outs=outs)
elif len(outs) and outs[-1] is not None:
x[a] = outs[-1]
return interpret(i + 2, x, argv=argv, outs=outs)
else:
# Here we want to block until the user applies input. This could be
# done easily with message passing for something similar.
x[a] = int(input('Enter: '))
return interpret(i + 2, x, argv=argv)
# output
elif op == '04':
a = x[i + 1]
mode_a = instruction[2]
a = a if mode_a == VAL else x[a]
outs.append(a)
return interpret(i + 2, x, argv=argv, outs=outs)
# jump-if-true
elif op == '05':
a, b = x[i + 1], x[i + 2]
mode_a, mode_b = instruction[2], instruction[1]
a = a if mode_a == VAL else x[a]
b = b if mode_b == VAL else x[b]
if a != 0:
return interpret(b, x, argv=argv, outs=outs)
else:
return interpret(i + 3, x, argv=argv, outs=outs)
# jump-if-false
elif op == '06':
a, b = x[i + 1], x[i + 2]
mode_a, mode_b = instruction[2], instruction[1]
a = a if mode_a == VAL else x[a]
b = b if mode_b == VAL else x[b]
if a == 0:
return interpret(b, x, argv=argv, outs=outs)
else:
return interpret(i + 3, x, argv=argv, outs=outs)
pass
# less than
elif op == '07':
a, b, out = x[i + 1], x[i + 2], x[i + 3]
mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[
0]
a = a if mode_a == VAL else x[a]
b = b if mode_b == VAL else x[b]
assert mode_out == POS
if a < b:
x[out] = 1
else:
x[out] = 0
return interpret(i + 4, x, argv=argv, outs=outs)
# equals
elif op == '08':
a, b, out = x[i + 1], x[i + 2], x[i + 3]
mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[
0]
a = a if mode_a == VAL else x[a]
b = b if mode_b == VAL else x[b]
assert mode_out == POS
if a == b:
x[out] = 1
else:
x[out] = 0
return interpret(i + 4, x, argv=argv, outs=outs)
elif op == '99':
return x[0]
else:
raise Exception('Unsupported opcode: {}.'.format(op))

View file

@ -0,0 +1,155 @@
from graphviz import Digraph
data = """6WF)DRK 2PT)PSM H42)FN8 1XR)LQD HRK)9KL TD6)H8W 98Z)BJM RCQ)LVG
RWQ)Q7H 2PS)X94 NHB)25X PXC)W57 L8L)MVX CFK)D8K R1B)43T PDY)QKX FQK)82K JJ6)MQJ
FB6)6V1 R28)5MZ BN2)5HN 6BQ)JVC W57)22C MQJ)DL2 MTC)84R RH8)CRN Y27)3GN CKQ)31C
R7V)9BK ZDY)PDY X2Q)Y6S Q8B)SAN 1Z3)PVT R87)57R KCJ)44X PWQ)9CB HLC)VYW HFP)9XS
X33)MC3 RYS)R7R JRF)VHW 79R)FXZ YQQ)STV 8J6)JWX Q6D)RV6 LL9)B4D 6R1)T1Z VK9)42M
PQP)17N K6C)HMK GLY)N47 KDW)CDC DQ4)RY5 SND)FDR 7YF)1VN MDT)B3S D3F)98Z 5VH)MR7
KNR)2L8 CJW)QDL FWY)14X SJD)79R COM)BXW T2B)FPB B2Q)BRJ Z21)HYC VHW)5XR WZ4)2JM
8HF)342 PYR)X9Y RKF)P43 S1S)9WT 2PB)BSB QF7)M9T HML)HMC 7J9)7Q6 8F1)29K DH1)NDM
1YC)PXC P32)HR7 PMX)7Y9 STV)SLW NYY)NF1 TG9)998 DMB)DLW XGL)1Z3 GK8)WCS YHR)HQC
9Q5)B6D R2T)CM5 6KC)J5G ZM9)L8L J8T)F89 3LN)YOU T2T)Z8F SCY)FKG 9W4)195 QLM)DD7
4QY)JCB WKM)3JF 693)YM8 61M)B6Y DSP)X2M YZ5)DPL BC9)3B1 BDB)JTG 3TJ)TW1 W5M)SF6
K4Q)X56 5HT)YHX YJG)DM5 68N)X2Q 2YP)DS5 BLK)MY3 6WV)VZ4 2JQ)ZT8 G93)V2W WN1)SBD
SS7)DY9 X56)8HP JY1)VS4 XQ6)L94 98Z)DMC V6S)NWT D9L)Y44 V6G)GVS JDW)FZW FJT)S38
L2Z)VPL 7ZX)DKK X2M)8WM YVZ)XWS HMK)P87 47M)TD6 TDZ)21T 19R)95B GD9)Q1L 9QX)DFR
Y64)XGN CRG)6VY V3L)61D RJ4)C9Z XXG)P53 VJ8)QTF CPQ)2M9 JRN)8V1 KMH)K94 DLW)VQ4
91W)2QQ G4B)RWQ 4P1)MKS K6G)DZ7 WCS)JR9 LXM)7RY 6ZB)K6G HMC)622 Z21)BLK Q6N)48V
66S)MK4 PDK)6WV Y6S)GY1 2L8)ZMG 42W)ZN6 6MS)8TZ JBY)STQ NSF)3ZM 5CV)X9N K4V)WFL
J6R)DT8 N3N)CX4 PTD)YXT F74)4T5 C51)3FW KRW)DS1 NWT)CKQ 195)6G6 HVQ)S18 Q7H)BKM
SKN)4D4 GK2)MLX MVX)TG9 YPK)RHQ Y9F)Z8W 42M)WNL 84R)6JP KNC)NHF FZW)PGM 3FW)HGX
DBK)FB6 45T)HLT L11)JVN HB5)K6C QH5)888 BTJ)J55 8BT)8ZS FR1)XGL S87)PS9 C4K)BN2
N2Q)18C KTF)ZM9 TN2)B2Q DF3)CFK 9T3)TMR P29)3P1 P1W)7SQ 4D4)1DJ LML)ZJ3 Q4L)RKF
MW2)79T LVG)CPQ BDC)JH5 DNZ)232 998)GTM YGS)4WH GY1)C51 J55)QBT B8Z)34W FJ2)H42
58J)326 T1Z)DCJ 1ZH)GLV 1YC)JG6 14K)22B RY5)QRY 7V2)2WT 4GQ)XHV ZJ3)TQ8 2G8)SN3
FPB)HMN SC4)57D 5LQ)R2T LXM)R8Z JQ6)G4B WNL)GK2 42M)P75 LM3)YPK ZN6)753 PN4)835
C4H)JY1 LR4)VD5 PSM)P1W VWL)C6C G2V)WBC 85M)R24 B1V)QW7 175)2PM Y1V)1ZH 34W)3MJ
WN7)TTB 3PV)CQD N7Y)9T3 223)8D4 RV6)LJ9 HFP)JRF VMT)DNB GJP)D3F J5G)KMS 7Q6)ZW2
YCB)JBY XGN)MNL 888)DSP X61)Q6N WT5)X12 SDN)FD1 2QC)54W V98)964 T7S)YVZ MLX)9VZ
FR8)QH5 TVQ)2PS 2PV)FHY F4S)MPT 3J9)JNB J6M)GDC Q4C)MJN 9VZ)BZK P2P)B69 WBC)M1W
D97)HPF JKB)9L4 593)6YJ RMB)4Q5 QZB)38C H12)6R1 MKY)DDD HGX)CRG P53)WY7 22B)GMM
44X)2D8 DT8)L7H 3Y2)D3S FB8)68N 3BC)1XR 4XF)TVQ VPL)R7V Z4V)JSK B3S)FW5 49Z)YQQ
99V)D13 54Q)SS7 CYC)TXH PQ3)78W X4M)G9H WFL)M99 ZYY)3Y2 12Y)PSW W38)P29 H8W)JJ6
P66)VPH GK2)45T H5F)FJT JDJ)SNV 14F)96Q JG6)TQ4 2L6)52Q SCY)CBJ 3GN)KNC KLM)XPR
DH1)QZB DMB)X7G DPL)7SX D97)N3N GNS)T95 53P)GW2 BHR)HNB YHX)XQV 2CR)Y1V C9D)Z7P
FN8)2PT 6LF)FCQ JNL)LQR SPV)YCB HGX)N83 VS4)8BT 5RH)FTX HYC)X2J 69V)J6S 9XS)PN4
SD7)5Q3 2RN)82D QRY)FFY K2Y)3X2 79Z)S2Z YN2)Y64 JKB)MDT KJ8)NDH N57)5VH 3XK)1Q1
SCH)FJ6 17N)GMP QR4)7V2 GLV)GLY NHF)ZDY QDL)S14 QF1)BMC ZLF)DHN 3JF)7TR MKS)GCY
964)91R 9L4)L5G RRX)6ZB CD7)73M 3X2)PGC HNB)S9Z L94)KLM 8MQ)SCR 18C)3TJ M4Y)BTJ
BC9)5YR TV5)SCY 2NX)8CC C9Z)MTC B69)3QP HR7)CHJ 8ZS)JRN 31C)TJW D43)4NH 93Q)X9X
T95)DNZ LQ5)BC9 9T5)S2C RP8)DH1 GCY)SD7 Y44)9B5 VG5)ZYY 7RY)V3L PWV)Q4L NF1)7YF
DRK)Y8V D13)GYG TW1)2PB ZVZ)2VV BRJ)V2V 9CB)Y7B MK4)9CJ TMR)6XS HWF)GK8 QTF)S1S
DFW)6LF N3S)WN1 N2Q)MSW CZ5)X61 FXZ)C4H SCQ)MF7 9LY)3LN 5MZ)PMX CN9)WF9 FHY)PR8
S38)NWH M29)G5S 4NH)GZJ 5YR)54H CLX)MNY TJD)HQL RRZ)4GQ YHB)CZ5 P37)93Q YJG)3Q3
95B)QMF CMQ)BLZ QD9)45M JSK)R28 YCW)CLX 8K3)JGB N8M)PQW P75)1HL XBS)T2T 22C)PVW
689)6MS FFY)RWX YHL)2G8 Y8V)4P1 Y7B)62Z YKJ)JDJ 1HL)5LQ PZ3)B1C 52Q)7HB 3Q2)ZV7
YBF)Z4V J95)SDH NM6)YBF 8YN)J3M J6S)KNR PVT)N4X SDH)RFW RFW)7Y1 JCB)52B 3MJ)H58
4QF)XHZ F62)DFW 7LJ)KDW JHL)C9D B4D)Q8B 342)YGS PFR)ZQT Z9K)TNS 8F8)WLB 94N)DMB
QBT)RYS 3VR)KRR 8D4)ST6 X9N)2PV 632)8K3 MX5)XNP 57D)Y27 18D)PQP D3F)RJ4 PLS)PBL
1JP)YDC 79V)BG2 S14)2NX 4Q5)NCQ FTX)555 2PM)KMH HQC)RMB 9Z9)BNZ XHV)Y94 7ZP)YHR
BNZ)49Z W6D)LX6 SLS)JL3 PVW)P9W Z1L)HB5 DS5)G2V Z9Q)RV8 DFR)LPJ 836)693 K94)VWL
HRG)836 J3V)593 52N)LPK 9KL)Y7M LX6)F7D JL3)511 L4G)D97 1RH)Y9F NJ2)LML GW2)9WV
8KZ)NRC XQV)G6D R8Z)QF7 326)HML R7R)8PM 622)YCW WQY)LGS NF1)FF3 5LQ)QF1 5XR)PTD
V2V)PFR 9T5)JQ6 CBQ)8KZ VZ4)HVQ TJW)DQT 9WT)5M6 CFK)YHL JR9)1JP Y1K)CF4 8WS)JPY
VYC)1D6 GKK)7J9 JTG)RRX 6V1)F74 1H5)QR4 SN3)NMG MF7)GQ1 RYK)SCH BNZ)9LY 1DJ)9LP
L6W)5BK FCQ)BFL DCJ)3RD MXD)8MQ RWX)1RH NBF)WKM K6C)WNH H58)L6W Y7B)BJH PGC)NBF
96Q)Q2W F7D)BSN 223)Z9K K94)VYC X9X)7M3 Q1M)3J9 QXF)XQ6 DD7)3Q2 Q1L)NHB 79T)LXQ
8TZ)M29 21T)Q4C B1C)NSF 8D8)FJ2 LJH)HGJ QS2)PS1 5KX)Z2L C6C)6BQ VQ2)2YP P87)N8M
ST5)L4G 8SP)W5M T4H)69V 9WF)GHS FF3)SND C5G)GKK VQ2)X4M P43)8J6 TD6)384 66V)CN9
CX4)T9T NCQ)2JQ 29K)K8K RY5)K4Q GQ3)T4H FNH)P32 3BC)PRQ 5HN)4QY M1W)BGT 84R)ST5
S45)CJW CK4)W7G SGX)19R S2C)7ZX DHN)W5Y 8D9)HM2 BSB)SPV D8K)DFV JHL)2L6 KYP)12Y
KDN)6X7 Y44)SQZ 6G6)SJD N7D)QGF Q84)8WJ F89)LL9 LYJ)2RN 25X)Q84 HM3)53P JNB)QD9
SLW)1DQ 384)3BC PR8)NGV 49N)7ZP 65H)LHJ 6XS)S45 ZMG)FR1 X2M)Y86 QD3)QLM P4R)PQ3
RTK)4M3 4YW)N7D R7V)M4M 73M)CBF DFV)64R Z7P)LMK HRG)Y1K 3ZM)BCZ WY7)QXP DMC)9Q5
PSW)1H5 8CC)TV5 TTB)S88 BZK)K2Y T2B)CBQ HJB)Y19 DQW)KML Z8W)8ZL PBL)5TK 1D6)MX5
3MJ)4YW MDT)HJB 62Z)X33 DZ7)BDC 9CJ)FRD 82D)KDN LK7)18D 9QQ)61M Y34)DZG J4T)6KC
971)QD3 511)GQ3 MJN)F62 RNM)NKG BGW)KJ8 DL2)1YH ZQT)RYZ 1YH)ZJ6 2WT)YYQ 7HB)DYQ
3BN)WQY 2M9)62D TSK)YR1 N7Y)VJ8 WZ4)FWT MNY)YN2 DYQ)RRZ 3RG)YT3 2SM)VK9 JH5)ZXH
GYG)K2M PKF)V6G JGB)S87 X94)N57 MSW)L2Z X4N)25G BLZ)4QF JPY)GD9 WLB)V6S KML)2SM
TXH)9X1 48V)KTR 8PM)WZ4 ZW2)967 PS9)3BN 4WH)9T5 8M1)R6V N7M)VWK S88)978 N4X)8KH
6VY)PLS NRC)874 QGF)QWJ NMG)J3V B8Z)WPF 45M)2QC KDW)VQ2 FZW)223 BXW)QXF FRD)PWV
8HP)4G7 KDN)YYL LHJ)SDN P6P)XMC W5Y)RYK HX8)KW3 Z2L)H12 WPF)T2B L7H)BGW MNL)17B
GHS)66V QKX)XWV FW5)W38 PDK)Y34 FKG)Q6D DQT)YJG 15G)79V 4VK)51Y BJH)LR4 48V)6GC
DM5)Y1F CM5)VG5 KB8)HRK 5HN)RCQ 6JP)SDQ LGH)NJ2 L94)N7Y 4Y2)ZLF 25G)C4K K8K)SLS
232)ZVZ GQ1)58J RV8)H5F 78W)565 YCF)8D9 DZG)99V N83)CKR TN2)ZCX NGV)8SP BSN)FTN
LPJ)94N 3Q3)Q1M JVX)971 54W)LGH 67Y)P66 R24)P37 3QP)QTY YHR)FLT GMP)NM6 NDH)632
PWV)8D8 LMK)3PV ZWJ)KB8 967)4VK 3B1)WN7 XWS)5CV YR1)FNH 565)4PH 5BK)V98 W5Y)FR8
PS1)HX8 38C)XXG XWV)1YC M4M)LQ5 S9Z)49N XMC)R1B YYL)VC9 GMM)SCQ LXQ)J95 51Y)RP8
HLT)XBS 82K)B8Z NR5)7K3 K2M)67Y SF6)W6D CF4)85M MC3)LXM HMN)RNM BFL)4XF MT2)PM4
VWK)JKB 3JF)ZTZ QWJ)9QQ KRR)TJD VYW)Z9Q CK4)QS2 8NQ)NR5 57R)BHR 8WM)YHB Y86)GNS
2Y2)Z21 X12)9QX LJ9)YKJ 3RD)8F1 7SQ)CK4 ZXH)3XK DDD)5KX ZCX)PYR GZJ)KXL KC5)52N
PM4)RYP 14X)ZWJ FJ6)175 17B)689 HQL)14F LQR)DBK LGS)4Y2 2QQ)SGR 2VV)8F8 J6S)LM3
RTP)YZ5 XDD)14K VQ4)MT2 KMH)KYC CKR)RTP VD5)MRM CM5)KRW BG3)XDD PGM)J4T MY3)JVX
Z8F)WNP BKM)WT5 FLT)KTF N7D)8M1 Y19)CMQ HPF)WDL 65H)JJP 2MQ)66S 4Q5)54Q Q2W)ZL4
QTY)659 MRM)9Z9 X2J)SC4 YWH)RB3 FTN)LYJ LMK)N7M SGX)15G KW3)FQK 3VV)JNL JWX)R8R
9Z3)9MB BMC)N3S W7G)Z1L SD7)MW2 376)RH8 NWT)JHL 7CD)N2Z KTR)HM3 1Q1)TDZ DY9)2CR
6YJ)14G FWT)JDW C2S)C5G SNV)J6M 5TK)YWH J3M)8HF HM2)GJP P9W)7CD 1VN)SGX KMS)RBK
64R)B1V 62D)3VV 61D)F4S XPR)SKN FJT)N3P 9WV)D43 TQ8)BDB 46H)K4V 8WJ)MXD NDM)9WF
8ZL)1QJ SCR)2MQ 7Y9)LJH VPH)MKY YDC)PDK 4G7)65H 2JM)NYY T9T)VMT 8M1)TSK G5S)X4N
6FH)KYP D98)DQW G6D)C2S 6X7)N2Q 1QJ)T7S ZL4)J8T 5BT)3VR 835)KCJ YM8)3RG Y7M)PWQ
54W)9W4 CBF)7LJ 4T5)8WS RHQ)HBK CQD)D98 HGJ)J6R JVC)79Z FD1)PKF VC9)5BT C4H)6WF
D3S)P6P MR7)BG3 R6V)DF3 9X1)NQ5 ZTZ)2Y2 8WM)HFP CDC)376 TQ4)M4Y 9MB)N1R HBK)DQ4
1DQ)CYC WNP)DM8 CBJ)LK7 ZT8)FWY LQD)PNN 555)9Z3 TNS)D9L QMF)L11 FR8)5RH WF9)R87
NKG)5HT L5G)91W N2Z)YV9 9B5)CD7 ZV7)8NQ ST6)74T ZJ6)CQV S18)47M 74T)8YN WNH)TN2
874)46H 3VV)PZ3 Y1F)42W MPT)2LP FDR)HWF X7G)RTK 52B)P4R RYP)G93 NWH)YCF 7TR)FB8
RWQ)6FH 8F8)HLC CRN)P2P B6D)KC5 PNN)HRG""".split()
# COM is the root in this tree
# parent :: Vertex -> [Edge] -> Maybe(Vertex)
def parent(x, xs):
for a, b in xs:
if b == x:
return a
return None
# parents :: Vertex -> [Edge] -> [Vertex]
def parents(x, xs):
parents = []
p = parent(x, xs)
while p:
parents.append(p)
p = parent(p, xs)
return parents
# alias Vertex :: String
# alias Edge :: (String, String)
# to_edge_list :: [String] -> [(String, String)]
def to_edge_list(xs):
"""Returns a list of tuples where (A, B) represents a directed edge from
vertex A to vertex B."""
return [(x[0:3], x[4:]) for x in xs]
# to_graphviz :: [Edge] -> String
def to_graphviz(xs):
d = Digraph()
for a, b in xs:
d.node(a, label=a)
d.edge(a, b)
return d.source
graph = to_edge_list(data)
you = parents('YOU', graph)
san = parents('SAN', graph)
# Distance from YOU to shared point with SAN
yd = 1
for i in range(len(you)):
if you[i] in san:
break
yd += 1
# Distance from SAN to shared point with YOU
sd = 1
for i in range(len(san)):
if san[i] in you:
break
sd += 1
print('Number of orbital transfers required: {}'.format(yd - 1 + sd - 1))

View file

@ -0,0 +1,49 @@
from day_5 import interpret
from itertools import permutations
# TODO: I may need to re-write this in Elixir modelling each amplifier as a
# `Process` and `Process.send`ing each amplifier the signals.
data = [
3, 8, 1001, 8, 10, 8, 105, 1, 0, 0, 21, 38, 59, 76, 89, 106, 187, 268, 349,
430, 99999, 3, 9, 1002, 9, 3, 9, 101, 2, 9, 9, 1002, 9, 4, 9, 4, 9, 99, 3,
9, 1001, 9, 5, 9, 1002, 9, 5, 9, 1001, 9, 2, 9, 1002, 9, 3, 9, 4, 9, 99, 3,
9, 1001, 9, 4, 9, 102, 4, 9, 9, 1001, 9, 3, 9, 4, 9, 99, 3, 9, 101, 4, 9,
9, 1002, 9, 5, 9, 4, 9, 99, 3, 9, 1002, 9, 3, 9, 101, 5, 9, 9, 1002, 9, 3,
9, 4, 9, 99, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9,
1002, 9, 2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9,
3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4,
9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 99, 3, 9, 1002, 9,
2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101,
1, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9,
101, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3,
9, 1001, 9, 2, 9, 4, 9, 99, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9,
4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9,
9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 102, 2,
9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 99, 3, 9,
1001, 9, 2, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9,
3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4,
9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, 1, 9,
9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 99, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 102,
2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9,
1001, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9,
3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 102, 2, 9, 9,
4, 9, 99
]
data_a, data_b, data_c, data_d, data_e = data[:], data[:], data[:], data[:], data[:]
# m = 0
# for a, b, c, d, e in permutations(range(5, 10)):
# answer = None
# z = 0
# while z is not None:
# print(a, b, c, d, e)
# print('---')
# v = interpret(0, data_a, argv=[a, z])
# print(v)
# w = interpret(0, data_b, argv=[b, v])
# x = interpret(0, data_c, argv=[c, w])
# y = interpret(0, data_d, argv=[d, x])
# z = interpret(0, data_e, argv=[e, y])
# m = max(m, z)

View file

@ -0,0 +1,40 @@
# Implementation for a problem from "Crack the Coding Interview".
#
# Dependencies:
# - python 2.7.16
# - entr 4.1
#
# To run the tests, run: `python 11_1.py`
# For a tight development loop, run: `echo 11_1.py | entr python /_`
#
# Author: William Carroll <wpcarro@gmail.com>
################################################################################
# Implementation
################################################################################
def insert_sorted(xs, ys):
"""
Merges `ys` into `xs` and ensures that the result is sorted.
Assumptions:
- `xs` and `ys` are both sorted.
- `xs` has enough unused space to accommodate each element in `ys`.
"""
for y in ys:
xi = xs.index(None) - 1
yi = xs.index(None)
xs[yi] = y
while xi != -1 and y < xs[xi]:
xs[xi], xs[yi] = xs[yi], xs[xi]
xi, yi = xi - 1, yi - 1
return xs
################################################################################
# Tests
################################################################################
assert insert_sorted([1, 3, 5, None, None], [2, 4]) == [1, 2, 3, 4, 5]
assert insert_sorted([None, None], [2, 4]) == [2, 4]
assert insert_sorted([None, None], [2, 4]) == [2, 4]
assert insert_sorted([1, 1, None, None], [0, 0]) == [0, 0, 1, 1]
assert insert_sorted([1, 1, None, None], [1, 1]) == [1, 1, 1, 1]
print('All tests pass!')

View file

@ -0,0 +1,11 @@
data Tree a = Node a [Tree a] deriving (Show)
withRoot :: [a] -> [Tree a]
withRoot xs = xs |> toThing |> fmap buildTree
buildTree :: (a, [a])
toTree :: [a] -> Tree a
toTree [x] = Node x []
toTree [x | xs] = Node x (toTree xs)

View file

@ -0,0 +1,87 @@
# This is practice for various types of list traversals that turn up.
xs = range(10)
n = len(xs)
print('---')
# pythonic left-to-right traversal
result = ''
for x in xs:
result += str(x)
print(result)
print('---')
# left-to-right traversal
result = ''
for i in range(n):
result += str(xs[i])
print(result)
print('---')
# right-to-left traversal
result = ''
for i in range(n):
result += str(xs[n - 1 - i])
print(result)
print('---')
# 2x left-to-right traversal
result = ''
for i in range(2 * n):
result += str(xs[i % n])
print(result)
print('---')
# 2x right-to-left traversal
result = ''
for i in range(2 * n):
result += str(xs[(n - 1 - i) % n])
print(result)
################################################################################
# Table traversals
################################################################################
table = [[row * 10 + i for i in range(10)] for row in range(3)]
row_ct = len(table)
col_ct = len(table[0])
print('---')
# 3x10 table traversal
result = ''
for row in table:
r = ''
for col in row:
r += '{:3d}'.format(col)
result += r + '\n'
print(result[0:-1])
print('---')
# 3x10 table traversal
result = ''
for row in range(row_ct):
r = ''
for col in range(col_ct):
r += '{:3d}'.format(table[row][col])
result += r + '\n'
print(result[0:-1])
print('---')
# 3x10 table traversal (reverse)
result = ''
for row in range(row_ct):
r = ''
for col in range(col_ct):
r += '{:3d}'.format(table[row_ct - 1 - row][col_ct - 1 - col])
result += r + '\n'
print(result)
print('---')
# 3x10 column-row traversal
result = ''
for col in range(col_ct):
r = ''
for row in range(row_ct):
r += '{:3d}'.format(table[row][col])
result += r + '\n'
print(result)

View file

@ -0,0 +1,145 @@
import unittest
from itertools import combinations
def balanced(xs):
"""Return True if `xs` contains no two values that differ by more than
one."""
if len(xs) == 0 or len(xs) == 1:
return True
if len(xs) == 2:
return math.abs(xs[0] - xs[1]) <= 1
else:
pass
def is_leaf(node):
return node.left is None and node.right is None
def is_balanced(tree_root):
"""Returns True if the difference between the depths of any two leaf nodes
does not exceed 1."""
depths = set()
populate_depths(tree_root, 0, depths)
# cartesian product - only the top half
for diff in set(abs(a - b) for a, b in combinations(depths, 2)):
if diff > 1:
return False
return True
def populate_depths(node, depth, depths):
if is_leaf(node):
depths.add(depth)
else:
if node.left is not None:
populate_depths(node.left, depth + 1, depths)
if node.right is not None:
populate_depths(node.right, depth + 1, depths)
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
class BinaryTreeNode(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def insert_left(self, value):
self.left = Test.BinaryTreeNode(value)
return self.left
def insert_right(self, value):
self.right = Test.BinaryTreeNode(value)
return self.right
def test_full_tree(self):
tree = Test.BinaryTreeNode(5)
left = tree.insert_left(8)
right = tree.insert_right(6)
left.insert_left(1)
left.insert_right(2)
right.insert_left(3)
right.insert_right(4)
result = is_balanced(tree)
self.assertTrue(result)
def test_both_leaves_at_the_same_depth(self):
tree = Test.BinaryTreeNode(3)
left = tree.insert_left(4)
right = tree.insert_right(2)
left.insert_left(1)
right.insert_right(9)
result = is_balanced(tree)
self.assertTrue(result)
def test_leaf_heights_differ_by_one(self):
tree = Test.BinaryTreeNode(6)
left = tree.insert_left(1)
right = tree.insert_right(0)
right.insert_right(7)
result = is_balanced(tree)
self.assertTrue(result)
def test_leaf_heights_differ_by_two(self):
tree = Test.BinaryTreeNode(6)
left = tree.insert_left(1)
right = tree.insert_right(0)
right_right = right.insert_right(7)
right_right.insert_right(8)
result = is_balanced(tree)
self.assertFalse(result)
def test_three_leaves_total(self):
tree = Test.BinaryTreeNode(1)
left = tree.insert_left(5)
right = tree.insert_right(9)
right.insert_left(8)
right.insert_right(5)
result = is_balanced(tree)
self.assertTrue(result)
def test_both_subtrees_superbalanced(self):
tree = Test.BinaryTreeNode(1)
left = tree.insert_left(5)
right = tree.insert_right(9)
right_left = right.insert_left(8)
right.insert_right(5)
right_left.insert_left(7)
result = is_balanced(tree)
self.assertFalse(result)
def test_both_subtrees_superbalanced_two(self):
tree = Test.BinaryTreeNode(1)
left = tree.insert_left(2)
right = tree.insert_right(4)
left.insert_left(3)
left_right = left.insert_right(7)
left_right.insert_right(8)
right_right = right.insert_right(5)
right_right_right = right_right.insert_right(6)
right_right_right.insert_right(9)
result = is_balanced(tree)
self.assertFalse(result)
def test_only_one_node(self):
tree = Test.BinaryTreeNode(1)
result = is_balanced(tree)
self.assertTrue(result)
def test_linked_list_tree(self):
tree = Test.BinaryTreeNode(1)
right = tree.insert_right(2)
right_right = right.insert_right(3)
right_right.insert_right(4)
result = is_balanced(tree)
self.assertTrue(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,32 @@
def test(x, i):
return x & (1 << i) != 0
def set(x, i):
return x | (1 << i)
def clear(x, i):
return x & ~(1 << i)
def toggle(x, i):
if test(x, i):
return clear(x, i)
else:
return set(x, i)
def test_single(x):
if x == 0:
return False
else:
return x & (x - 1) == 0
print(test(0b1010, 3))
print('{0:b}'.format(set(0b1010, 1)))
print('{0:b}'.format(clear(0b1010, 1)))
print('{0:b}'.format(toggle(0b1010, 2)))
print(test_single(0b1010))
print(test_single(0b1000))

View file

@ -0,0 +1,63 @@
import unittest
################################################################################
# Solution
################################################################################
# is_valid :: String -> Boolean
def is_valid(xs):
s = []
seeking = {
'}': '{',
']': '[',
')': '(',
}
openers = seeking.values()
closers = seeking.keys()
for c in xs:
if c in openers:
s.append(c)
elif c in closers:
if not s:
return False
elif s[-1] != seeking.get(c):
return False
else:
s.pop()
return len(s) == 0
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_valid_short_code(self):
result = is_valid('()')
self.assertTrue(result)
def test_valid_longer_code(self):
result = is_valid('([]{[]})[]{{}()}')
self.assertTrue(result)
def test_interleaved_openers_and_closers(self):
result = is_valid('([)]')
self.assertFalse(result)
def test_mismatched_opener_and_closer(self):
result = is_valid('([][]}')
self.assertFalse(result)
def test_missing_closer(self):
result = is_valid('[[]()')
self.assertFalse(result)
def test_extra_closer(self):
result = is_valid('[[]]())')
self.assertFalse(result)
def test_empty_string(self):
result = is_valid('')
self.assertTrue(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,121 @@
import unittest
################################################################################
# Implementation
################################################################################
# is_leaf :: Node(a) -> Boolean
def is_leaf(node):
return not node.left and not node.right
# is_binary_search_tree :: Node(Integer) -> Set(Int) -> Set(Int) -> Boolean
def is_binary_search_tree_a(node, la=set(), ra=set()):
"""My first solution for this problem."""
for x in la:
if not node.value < x:
return False
for x in ra:
if not node.value > x:
return False
if is_leaf(node):
return True
elif not node.left:
return is_binary_search_tree(
node.right,
la=la,
ra=ra ^ {node.value},
)
elif not node.right:
return is_binary_search_tree(node.left, la=la ^ {node.value}, ra=ra)
else:
return all([
is_binary_search_tree(node.left, la=la ^ {node.value}, ra=ra),
is_binary_search_tree(node.right, la=la, ra=ra ^ {node.value})
])
# is_binary_search_tree :: Node(Int) -> Maybe(Int) -> Maybe(Int) -> Boolean
def is_binary_search_tree(node, lb=None, ub=None):
if lb:
if node.value < lb:
return False
if ub:
if node.value > ub:
return False
if is_leaf(node):
return True
elif not node.right:
return is_binary_search_tree(node.left, lb=lb, ub=node.value)
elif not node.left:
return is_binary_search_tree(node.right, lb=node.value, ub=ub)
else:
return is_binary_search_tree(
node.left, lb=lb, ub=node.value) and is_binary_search_tree(
node.right, lb=node.value, ub=ub)
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
class BinaryTreeNode(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def insert_left(self, value):
self.left = Test.BinaryTreeNode(value)
return self.left
def insert_right(self, value):
self.right = Test.BinaryTreeNode(value)
return self.right
def test_valid_full_tree(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(30)
right = tree.insert_right(70)
left.insert_left(10)
left.insert_right(40)
right.insert_left(60)
right.insert_right(80)
result = is_binary_search_tree(tree)
self.assertTrue(result)
def test_both_subtrees_valid(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(30)
right = tree.insert_right(80)
left.insert_left(20)
left.insert_right(60)
right.insert_left(70)
right.insert_right(90)
result = is_binary_search_tree(tree)
self.assertFalse(result)
def test_descending_linked_list(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(40)
left_left = left.insert_left(30)
left_left_left = left_left.insert_left(20)
left_left_left.insert_left(10)
result = is_binary_search_tree(tree)
self.assertTrue(result)
def test_out_of_order_linked_list(self):
tree = Test.BinaryTreeNode(50)
right = tree.insert_right(70)
right_right = right.insert_right(60)
right_right.insert_right(80)
result = is_binary_search_tree(tree)
self.assertFalse(result)
def test_one_node_tree(self):
tree = Test.BinaryTreeNode(50)
result = is_binary_search_tree(tree)
self.assertTrue(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,91 @@
import unittest
################################################################################
# Implementation
################################################################################
def is_first_come_first_served(to, di, xs):
# All the guards, assertions we should need.
if to == di == xs == []:
return True
elif to == di == []:
return False
elif to == []:
return di == xs
elif to == []:
return di == xs
elif di == []:
return to == xs
elif xs == []:
return False
elif len(xs) != (len(to) + len(di)):
return False
fst, snd = to, di
if xs[0] == to[0]:
fst, snd = to, di
elif xs[0] == di[0]:
fst, snd = di, to
else:
return False
fst_done, snd_done = False, False
fi, si = 1, 0
for i in range(1, len(xs)):
# Short-circuit and avoid index-out-of-bounds without introducing overly
# defensive, sloppy code.
if fst_done:
return snd[si:] == xs[i:]
elif snd_done:
return fst[fi:] == xs[i:]
if fst[fi] == xs[i]:
fi += 1
elif snd[si] == xs[i]:
si += 1
else:
return False
fst_done, snd_done = fi == len(fst), si == len(snd)
return True
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_both_registers_have_same_number_of_orders(self):
result = is_first_come_first_served([1, 4, 5], [2, 3, 6],
[1, 2, 3, 4, 5, 6])
self.assertTrue(result)
def test_registers_have_different_lengths(self):
result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 2, 6, 3, 5])
self.assertFalse(result)
def test_one_register_is_empty(self):
result = is_first_come_first_served([], [2, 3, 6], [2, 3, 6])
self.assertTrue(result)
def test_served_orders_is_missing_orders(self):
result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 6, 3, 5])
self.assertFalse(result)
def test_served_orders_has_extra_orders(self):
result = is_first_come_first_served([1, 5], [2, 3, 6],
[1, 2, 3, 5, 6, 8])
self.assertFalse(result)
def test_one_register_has_extra_orders(self):
result = is_first_come_first_served([1, 9], [7, 8], [1, 7, 8])
self.assertFalse(result)
def test_one_register_has_unserved_orders(self):
result = is_first_come_first_served([55, 9], [7, 8], [1, 7, 8, 9])
self.assertFalse(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,71 @@
import unittest
from math import floor
################################################################################
# Solution
################################################################################
def max_duffel_bag_value(xs, cap):
ct = (cap + 1)
maxes = [0] * ct
for c in range(cap + 1):
for w, v in xs:
if w == 0 and v > 0:
return float('inf')
if w == c:
maxes[c:] = [max(maxes[c], v)] * (ct - c)
elif w < c:
d = c - w
maxes[c:] = [max(maxes[c], v + maxes[d])] * (ct - c)
else:
continue
return maxes[cap]
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_one_cake(self):
actual = max_duffel_bag_value([(2, 1)], 9)
expected = 4
self.assertEqual(actual, expected)
def test_two_cakes(self):
actual = max_duffel_bag_value([(4, 4), (5, 5)], 9)
expected = 9
self.assertEqual(actual, expected)
def test_only_take_less_valuable_cake(self):
actual = max_duffel_bag_value([(4, 4), (5, 5)], 12)
expected = 12
self.assertEqual(actual, expected)
def test_lots_of_cakes(self):
actual = max_duffel_bag_value([(2, 3), (3, 6), (5, 1), (6, 1), (7, 1),
(8, 1)], 7)
expected = 12
self.assertEqual(actual, expected)
def test_value_to_weight_ratio_is_not_optimal(self):
actual = max_duffel_bag_value([(51, 52), (50, 50)], 100)
expected = 100
self.assertEqual(actual, expected)
def test_zero_capacity(self):
actual = max_duffel_bag_value([(1, 2)], 0)
expected = 0
self.assertEqual(actual, expected)
def test_cake_with_zero_value_and_weight(self):
actual = max_duffel_bag_value([(0, 0), (2, 1)], 7)
expected = 3
self.assertEqual(actual, expected)
def test_cake_with_non_zero_value_and_zero_weight(self):
actual = max_duffel_bag_value([(0, 5)], 5)
expected = float('inf')
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,57 @@
import unittest
from math import floor
################################################################################
# Solution
################################################################################
# change_possibilities :: Int -> [Int] -> Int
def change_possibilities(n, xs):
combinations = [0] * (n + 1)
combinations[0] = 1
for x in xs:
for i in range(len(combinations)):
if i >= x:
combinations[i] += combinations[i - x]
return combinations[n]
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_sample_input(self):
actual = change_possibilities(4, (1, 2, 3))
expected = 4
self.assertEqual(actual, expected)
def test_one_way_to_make_zero_cents(self):
actual = change_possibilities(0, (1, 2))
expected = 1
self.assertEqual(actual, expected)
def test_no_ways_if_no_coins(self):
actual = change_possibilities(1, ())
expected = 0
self.assertEqual(actual, expected)
def test_big_coin_value(self):
actual = change_possibilities(5, (25, 50))
expected = 0
self.assertEqual(actual, expected)
def test_big_target_amount(self):
actual = change_possibilities(50, (5, 10))
expected = 6
self.assertEqual(actual, expected)
def test_change_for_one_dollar(self):
actual = change_possibilities(100, (1, 5, 10, 25, 50))
expected = 292
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,78 @@
from itertools import product
from random import choice
from time import sleep
from os import system
from math import floor
from colorama import Back, Fore, Style
################################################################################
# Simulation of Conway's Game of Life. The goal here was to write this with a
# small amount of code as a proof-of-concept that could be run in the terminal.
#
# If you'd like to tinker with the rules, see the conditionals defined in the
# `advance/1` function. For other parameters, like the board size and refresh
# rate, refer to the while-loop defined at the bottom of this file.
################################################################################
def init_board(n, init_alive_percentage):
"""Initialize a board of size `n` by `n`. Supply a percentage,
`init_alive_percentage`, representing the number of cells in the board that
should be alive from the start."""
alive_count = floor(n * init_alive_percentage)
distribution = [True] * alive_count + [False] * (n - alive_count)
return [[choice(distribution) for _ in range(n)] for _ in range(n)]
def neighbors(coord, board):
"""Return the neighbors for a given `coord` on a `board`."""
n = len(board)
row, col = coord
return [
board[(row + row_d) % n][(col + col_d) % n]
for row_d, col_d in product([-1, 0, 1], [-1, 0, 1])
if (row_d, col_d) != (0, 0)
]
def advance(board):
"""Advance the state of the `board` from T[n] to T[n+1]."""
n = len(board)
new_board = [[False for _ in range(n)] for _ in range(n)]
for row in range(n):
for col in range(n):
alive_count = len([x for x in neighbors((row, col), board) if x])
# Loneliness
if alive_count == 0:
new_board[row][col] = False
# Status Quo
elif alive_count == 1:
new_board[row][col] = board[row][col]
# Cooperation
elif alive_count == 2:
new_board[row][col] = True
# Resource starvation
elif alive_count >= 3:
new_board[row][col] = False
return new_board
def print_board(board):
"""Print the game `board` in a human-readable way."""
result = ''
for row in board:
for col in row:
if col:
result += Back.GREEN + '1 ' + Style.RESET_ALL
else:
result += Back.RED + '0 ' + Style.RESET_ALL
result += '\n'
print(result)
board = init_board(100, 0.50)
while True:
system('clear')
print_board(board)
sleep(0.15)
board = advance(board)

View file

@ -0,0 +1,60 @@
import unittest
################################################################################
# Solution
################################################################################
def delete_node(x):
if not x.next:
raise Exception('Cannot delete the last node in a linked list.')
else:
x.value = x.next.value
x.next = x.next.next
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
class LinkedListNode(object):
def __init__(self, value, next=None):
self.value = value
self.next = next
def get_values(self):
node = self
values = []
while node is not None:
values.append(node.value)
node = node.next
return values
def setUp(self):
self.fourth = Test.LinkedListNode(4)
self.third = Test.LinkedListNode(3, self.fourth)
self.second = Test.LinkedListNode(2, self.third)
self.first = Test.LinkedListNode(1, self.second)
def test_node_at_beginning(self):
delete_node(self.first)
actual = self.first.get_values()
expected = [2, 3, 4]
self.assertEqual(actual, expected)
def test_node_in_middle(self):
delete_node(self.second)
actual = self.first.get_values()
expected = [1, 3, 4]
self.assertEqual(actual, expected)
def test_node_at_end(self):
with self.assertRaises(Exception):
delete_node(self.fourth)
def test_one_node_in_list(self):
unique = Test.LinkedListNode(1)
with self.assertRaises(Exception):
delete_node(unique)
unittest.main(verbosity=2)

View file

@ -0,0 +1,65 @@
from random import choice
class Node(object):
def __init__(self, value=None, left=None, right=None):
self.value = value
self.left = left
self.right = left
def p(node, indent=0):
print(indent * ' ' + '|-' + str(node.value))
if node.left is not None:
p(node.left, indent=indent + 2)
if node.right is not None:
p(node.right, indent=indent + 2)
# read trees (i.e. traversing, parsing)
# write trees (i.e. generating, printing)
def random(d=0):
left = None
right = None
if choice([True, False]):
left = random(d + 1)
if choice([True, False]):
right = random(d + 1)
return Node(
value=d,
left=left,
right=right,
)
################################################################################
# DFTs can be:
# - imperative (mutable)
# - functional (immutable)
# - iterative
# - recursive
################################################################################
# Iterative
def traverse(node, f):
stack = [(node, 0)]
while len(stack):
node, depth = stack.pop()
f(node, depth)
print(depth)
if node.left is not None:
stack.append((node.left, depth + 1))
if node.right is not None:
stack.append((node.right, depth + 1))
print('----------------------------------------------------------------------')
for _ in range(10):
traverse(random(), lambda _, d: print(d))
print()

View file

@ -0,0 +1,48 @@
from collections import deque
from heapq import heappush, heappop
from fixtures import weighted_graph
def put(t, x, xs):
if t == 'stack':
return xs.append(x)
if t == 'queue':
return xs.append(x)
if t == 'priority':
return heappush(xs, x)
def pop(t, xs):
if t == 'stack':
return xs.pop()
if t == 'queue':
return xs.popleft()
if t == 'priority':
return heappop(xs)
# shortest_path :: Vertex -> Vertex -> Graph -> [Vertex]
def shortest_path(a, b, g):
"""Returns the shortest path from vertex a to vertex b in graph g."""
t = 'priority'
xs = []
seen = set()
# Map(Weight, [Vertex])
m = {}
put(t, (0, [a], a), xs)
while xs:
w0, path, v = pop(t, xs)
seen.add(v)
if v == b:
m[w0] = path
for w1, x in g.get(v):
if x not in seen:
put(t, (w0 + w1, path + [x], x), xs)
return m
print(shortest_path('a', 'f', graph_a))

View file

@ -0,0 +1,56 @@
import unittest
################################################################################
# Solution
################################################################################
def find_duplicate(xs):
self_ref_count = 0
for i in range(len(xs)):
if xs[i] == i + 1:
self_ref_count += 1
hops = len(xs) - 1 - self_ref_count
current = xs[-1]
while hops > 0:
current = xs[current - 1]
hops -= 1
return current
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
# TODO: Debug why this fails.
def test_darren_from_interview_cake(self):
actual = find_duplicate([4, 1, 8, 3, 2, 7, 6, 5, 4])
expected = 4
self.assertEqual(actual, expected)
def test_just_the_repeated_number(self):
actual = find_duplicate([1, 1])
expected = 1
self.assertEqual(actual, expected)
def test_short_list(self):
actual = find_duplicate([1, 2, 3, 2])
expected = 2
self.assertEqual(actual, expected)
def test_last_cycle(self):
actual = find_duplicate([3, 4, 2, 3, 1, 5])
expected = 3
self.assertEqual(actual, expected)
def test_medium_list(self):
actual = find_duplicate([1, 2, 5, 5, 5, 5])
expected = 5
self.assertEqual(actual, expected)
def test_long_list(self):
actual = find_duplicate([4, 1, 4, 8, 3, 2, 7, 6, 5])
expected = 4
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,61 @@
from math import floor
import unittest
################################################################################
# Solution
################################################################################
def bounds(r):
ct = len(r)
if ct % 2 == 0:
h = int(ct / 2)
return ct, h
else:
h = floor(ct / 2)
return ct, h
def find_repeat(xs):
ct, h = bounds(xs)
rl = range(1, h + 1)
rr = range(h + 1, ct)
while True:
nl = len([None for x in xs if x in rl])
nr = len([None for x in xs if x in rr])
branch = rl if nl > nr else rr
if len(branch) == 1:
return branch[0]
ct, h = bounds(branch)
rl = range(branch[0], branch[0])
rr = range(branch[0] + h, branch[-1] + 1)
raise Exception(
'We could not find any duplicates in xs. Perhaps xs did not adhere to the usage contract.'
)
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_just_the_repeated_number(self):
actual = find_repeat([1, 1])
expected = 1
self.assertEqual(actual, expected)
def test_short_list(self):
actual = find_repeat([1, 2, 3, 2])
expected = 2
self.assertEqual(actual, expected)
def test_medium_list(self):
actual = find_repeat([1, 2, 5, 5, 5, 5])
expected = 5
self.assertEqual(actual, expected)
def test_long_list(self):
actual = find_repeat([4, 1, 4, 8, 3, 2, 7, 6, 5])
expected = 4
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,59 @@
import unittest
################################################################################
# Solution
################################################################################
def find_rotation_point(xs):
"""Usage of `visited` here is a hack, but works for the test cases
(gulp)."""
i = 0
j = round(len(xs) / 2)
result = None
visited = set()
while not result:
if i in visited:
i += 1
if j in visited:
j -= 1
visited.add(i)
visited.add(j)
if xs[j - 1] > xs[j]:
result = j
elif xs[i] < xs[j]:
i = j
j += round((len(xs) - j) / 2)
elif xs[i] >= xs[j]:
i = j
j -= round((j - i) / 2)
return result
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_small_list(self):
actual = find_rotation_point(['cape', 'cake'])
expected = 1
self.assertEqual(actual, expected)
def test_medium_list(self):
actual = find_rotation_point(
['grape', 'orange', 'plum', 'radish', 'apple'])
expected = 4
self.assertEqual(actual, expected)
def test_large_list(self):
actual = find_rotation_point([
'ptolemaic', 'retrograde', 'supplant', 'undulate', 'xenoepist',
'asymptote', 'babka', 'banoffee', 'engender', 'karpatka',
'othellolagkage'
])
expected = 5
self.assertEqual(actual, expected)
# Are we missing any edge cases?
unittest.main(verbosity=2)

View file

@ -0,0 +1,45 @@
import unittest
################################################################################
# Solution
################################################################################
def find_unique_delivery_id(xs):
a = 0
for x in xs:
a ^= x
return a
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_one_drone(self):
actual = find_unique_delivery_id([1])
expected = 1
self.assertEqual(actual, expected)
def test_unique_id_comes_first(self):
actual = find_unique_delivery_id([1, 2, 2])
expected = 1
self.assertEqual(actual, expected)
def test_unique_id_comes_last(self):
actual = find_unique_delivery_id([3, 3, 2, 2, 1])
expected = 1
self.assertEqual(actual, expected)
def test_unique_id_in_middle(self):
actual = find_unique_delivery_id([3, 2, 1, 2, 3])
expected = 1
self.assertEqual(actual, expected)
def test_many_drones(self):
actual = find_unique_delivery_id(
[2, 5, 4, 8, 6, 3, 1, 4, 2, 3, 6, 5, 1])
expected = 8
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,110 @@
# Using this module to store commonly used, but annoying to create, data
# structures for my test inputs.
#
# Use like:
# from fixtures import graph_a
################################################################################
# Constants
################################################################################
edge_list = [
('a', 'b'),
('a', 'c'),
('a', 'e'),
('b', 'c'),
('b', 'd'),
('c', 'e'),
('d', 'f'),
('e', 'd'),
('e', 'f'),
]
unweighted_graph = {
'a': {'b', 'c', 'e'},
'b': {'c', 'd'},
'c': {'e'},
'd': {'f'},
'e': {'d', 'f'},
'f': set(),
}
adjacencies = {
'a': {
'a': False,
'b': False
},
'a': [],
'a': [],
'a': [],
'a': [],
'a': [],
'a': [],
}
weighted_graph = {
'a': {(4, 'b'), (2, 'c'), (4, 'e')},
'b': {(5, 'c'), (10, 'd')},
'c': {(3, 'e')},
'd': {(11, 'f')},
'e': {(4, 'd'), (5, 'f')},
'f': set(),
}
# This is `weighted_graph` with each of its weighted edges "expanded".
expanded_weights_graph = {
'a': ['b-1', 'c-1', 'e-1'],
'b-1': ['b-2'],
'b-2': ['b-3'],
'b-3': ['b'],
'c-1': ['c'],
'e-1': ['e-2'],
'e-2': ['e-3'],
'e-3': ['e'],
# and so on...
}
unweighted_digraph = {
'5': {'2', '0'},
'4': {'0', '1'},
'3': {'1'},
'2': {'3'},
'1': set(),
'0': set(),
}
################################################################################
# Functions
################################################################################
def vertices(xs):
result = set()
for a, b in xs:
result.add(a)
result.add(b)
return result
def edges_to_neighbors(xs):
result = {v: set() for v in vertices(xs)}
for a, b in xs:
result[a].add(b)
return result
def neighbors_to_edges(xs):
result = []
for k, ys in xs.items():
for y in ys:
result.append((k, y))
return result
def edges_to_adjacencies(xs):
return xs
# Skipping handling adjacencies because I cannot think of a reasonable use-case
# for it when the vertex labels are items other than integers. I can think of
# ways of handling this, but none excite me.

View file

@ -0,0 +1,180 @@
import unittest
from collections import deque
################################################################################
# Solution
################################################################################
class GraphNode:
def __init__(self, label):
self.label = label
self.neighbors = set()
self.color = None
# color_graph :: G(V, E) -> Set(Color) -> IO ()
def color_graph(graph, colors):
q = deque()
seen = set()
q.append(graph[0])
while q:
node = q.popleft()
illegal = {n.color for n in node.neighbors}
for x in colors:
if x not in illegal:
node.color = x
seen.add(node)
for x in node.neighbors:
if x not in seen:
q.append(x)
# TODO: Is this the best way to traverse separate graphs?
for x in graph:
if x not in seen:
q.append(x)
return 0
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def setUp(self):
self.colors = frozenset([
'red',
'green',
'blue',
'orange',
'yellow',
'white',
])
def assertGraphColoring(self, graph, colors):
self.assertGraphHasColors(graph, colors)
self.assertGraphColorLimit(graph)
for node in graph:
self.assertNodeUniqueColor(node)
def assertGraphHasColors(self, graph, colors):
for node in graph:
msg = 'Node %r color %r not in %r' % (node.label, node.color,
colors)
self.assertIn(node.color, colors, msg=msg)
def assertGraphColorLimit(self, graph):
max_degree = 0
colors_found = set()
for node in graph:
degree = len(node.neighbors)
max_degree = max(degree, max_degree)
colors_found.add(node.color)
max_colors = max_degree + 1
used_colors = len(colors_found)
msg = 'Used %d colors and expected %d at most' % (used_colors,
max_colors)
self.assertLessEqual(used_colors, max_colors, msg=msg)
def assertNodeUniqueColor(self, node):
for adjacent in node.neighbors:
msg = 'Adjacent nodes %r and %r have the same color %r' % (
node.label,
adjacent.label,
node.color,
)
self.assertNotEqual(node.color, adjacent.color, msg=msg)
def test_line_graph(self):
node_a = GraphNode('a')
node_b = GraphNode('b')
node_c = GraphNode('c')
node_d = GraphNode('d')
node_a.neighbors.add(node_b)
node_b.neighbors.add(node_a)
node_b.neighbors.add(node_c)
node_c.neighbors.add(node_b)
node_c.neighbors.add(node_d)
node_d.neighbors.add(node_c)
graph = [node_a, node_b, node_c, node_d]
tampered_colors = list(self.colors)
color_graph(graph, tampered_colors)
self.assertGraphColoring(graph, self.colors)
def test_separate_graph(self):
node_a = GraphNode('a')
node_b = GraphNode('b')
node_c = GraphNode('c')
node_d = GraphNode('d')
node_a.neighbors.add(node_b)
node_b.neighbors.add(node_a)
node_c.neighbors.add(node_d)
node_d.neighbors.add(node_c)
graph = [node_a, node_b, node_c, node_d]
tampered_colors = list(self.colors)
color_graph(graph, tampered_colors)
self.assertGraphColoring(graph, self.colors)
def test_triangle_graph(self):
node_a = GraphNode('a')
node_b = GraphNode('b')
node_c = GraphNode('c')
node_a.neighbors.add(node_b)
node_a.neighbors.add(node_c)
node_b.neighbors.add(node_a)
node_b.neighbors.add(node_c)
node_c.neighbors.add(node_a)
node_c.neighbors.add(node_b)
graph = [node_a, node_b, node_c]
tampered_colors = list(self.colors)
color_graph(graph, tampered_colors)
self.assertGraphColoring(graph, self.colors)
def test_envelope_graph(self):
node_a = GraphNode('a')
node_b = GraphNode('b')
node_c = GraphNode('c')
node_d = GraphNode('d')
node_e = GraphNode('e')
node_a.neighbors.add(node_b)
node_a.neighbors.add(node_c)
node_b.neighbors.add(node_a)
node_b.neighbors.add(node_c)
node_b.neighbors.add(node_d)
node_b.neighbors.add(node_e)
node_c.neighbors.add(node_a)
node_c.neighbors.add(node_b)
node_c.neighbors.add(node_d)
node_c.neighbors.add(node_e)
node_d.neighbors.add(node_b)
node_d.neighbors.add(node_c)
node_d.neighbors.add(node_e)
node_e.neighbors.add(node_b)
node_e.neighbors.add(node_c)
node_e.neighbors.add(node_d)
graph = [node_a, node_b, node_c, node_d, node_e]
tampered_colors = list(self.colors)
color_graph(graph, tampered_colors)
self.assertGraphColoring(graph, self.colors)
def test_loop_graph(self):
node_a = GraphNode('a')
node_a.neighbors.add(node_a)
graph = [node_a]
tampered_colors = list(self.colors)
with self.assertRaises(Exception):
color_graph(graph, tampered_colors)
unittest.main(verbosity=2)

View file

@ -0,0 +1,39 @@
from graphviz import Digraph
from collections import deque
from fixtures import weighted_graph
# There are three ways to model a graph:
# 1. Edge list: [(Vertex, Vertex)]
# 2. Neighbors table: Map(Vertex, [Vertex])
# 3. Adjacency matrix: [[Boolean]]
#
# The following graph is a neighbors table.
# to_graphviz :: Vertex -> Map(Vertex, [(Vertex, Weight)]) -> String
def to_graphviz(start, g):
"""Compiles the graph into GraphViz."""
d = Digraph()
q = deque()
seen = set()
q.append(start)
while q:
v = q.popleft()
if v in seen:
continue
d.node(v, label=v)
for w, x in g[v]:
d.edge(v, x, label=str(w))
q.append(x)
seen.add(v)
return d.source
with open('/tmp/test.gv', 'w') as f:
src = to_graphviz('a', weighted_graph)
f.write(src)
print('/tmp/test.gv created!')

View file

@ -0,0 +1,89 @@
import unittest
################################################################################
# Solution
################################################################################
# f :: [Int] -> Int
def highest_product_of_3(xs):
"""Here we're greedily storing:
- current max
- largest product of two
- largest positive number
- second largest positive number
- largest negative number
"""
if len(xs) < 3:
raise Exception
cm = None
ld = xs[0] * xs[1]
l2 = min(xs[0], xs[1])
if xs[0] < 0 or xs[1] < 0:
ln = min(xs[0], xs[1])
else:
ln = 1
l = max(xs[0], xs[1])
for x in xs[2:]:
if not cm:
cm = max(x * ln * l, ld * x, x * l * l2) # beware
ld = max(ld, x * ln, x * l)
ln = min(ln, x)
l = max(l, x)
if x < l:
l2 = max(l2, x)
else:
cm = max(cm, x * ln * l, x * ld, x * l * l2)
ld = max(ld, x * ln, x * l)
ln = min(ln, x)
l = max(l, x)
if x < l:
l2 = max(l2, x)
return cm
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_short_list(self):
actual = highest_product_of_3([1, 2, 3, 4])
expected = 24
self.assertEqual(actual, expected)
def test_longer_list(self):
actual = highest_product_of_3([6, 1, 3, 5, 7, 8, 2])
expected = 336
self.assertEqual(actual, expected)
def test_list_has_one_negative(self):
actual = highest_product_of_3([-5, 4, 8, 2, 3])
expected = 96
self.assertEqual(actual, expected)
def test_list_has_two_negatives(self):
actual = highest_product_of_3([-10, 1, 3, 2, -10])
expected = 300
self.assertEqual(actual, expected)
def test_list_is_all_negatives(self):
actual = highest_product_of_3([-5, -1, -3, -2])
expected = -6
self.assertEqual(actual, expected)
def test_error_with_empty_list(self):
with self.assertRaises(Exception):
highest_product_of_3([])
def test_error_with_one_number(self):
with self.assertRaises(Exception):
highest_product_of_3([1])
def test_error_with_two_numbers(self):
with self.assertRaises(Exception):
highest_product_of_3([1, 1])
unittest.main(verbosity=2)

View file

@ -0,0 +1,35 @@
# possible :: Int -> [Int] -> Bool
def possible(flight_duration, film_durations):
seeking = set()
for x in film_durations:
if x in seeking:
return True
else:
seeking.add(flight_duration - x)
return False
should = [
(10, [1, 9, 8, 8, 8]),
(10, [1, 9]),
(10, [1, 9, 5, 5, 6]),
(1, [0.5, 0.5]),
(1, [0.5, 0.5]),
]
for a, b in should:
print("Testing: %s %s" % (a, b))
assert possible(a, b)
shouldnt = [
(10, [1, 10, 1, 2, 1, 12]),
(1, [0.25, 0.25, 0.25, 0.25]),
(5, [1, 2, 2]),
]
for a, b in shouldnt:
print("Testing: %s %s" % (a, b))
assert not possible(a, b)
print("Tests pass")

View file

@ -0,0 +1,38 @@
import unittest
from math import floor
def knapify(xs, capacity=None):
assert capacity is not None
n = len(xs)
# For 0/1 Knapsack, we must use a table, since this will encode which values
# work for which items. This is cleaner than including a separate data
# structure to capture it.
maxes = [[0 for x in range(capacity + 1)] for x in range(n + 1)]
# Build table maxes[][] in bottom up manner
for row in range(n + 1):
for col in range(capacity + 1):
if row == 0 or col == 0:
maxes[row][col] = 0
elif xs[row - 1][0] <= col:
maxes[row][col] = max(
xs[row - 1][1] + maxes[row - 1][col - xs[row - 1][0]],
maxes[row - 1][col])
else:
maxes[row][col] = maxes[row - 1][col]
return maxes[-1][capacity]
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_one_cake(self):
actual = knapify([(3, 10), (2, 15), (7, 2), (12, 20)], capacity=12)
expected = None
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,82 @@
import unittest
################################################################################
# Solution
################################################################################
def length(x):
if not x:
return 0
else:
count = 1
while x:
x = x.next
count += 1
return count
def kth_to_last_node(k, x):
hops = length(x) - 1
dest = hops - k
if k == 0:
raise Exception("Our God doesn't support this kind of behavior.")
if dest < 0:
raise Exception('Value k to high for list.')
while dest > 0:
x = x.next
dest -= 1
return x
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
class LinkedListNode(object):
def __init__(self, value, next=None):
self.value = value
self.next = next
def get_values(self):
node = self
values = []
while node is not None:
values.append(node.value)
node = node.next
return values
def setUp(self):
self.fourth = Test.LinkedListNode(4)
self.third = Test.LinkedListNode(3, self.fourth)
self.second = Test.LinkedListNode(2, self.third)
self.first = Test.LinkedListNode(1, self.second)
def test_first_to_last_node(self):
actual = kth_to_last_node(1, self.first)
expected = self.fourth
self.assertEqual(actual, expected)
def test_second_to_last_node(self):
actual = kth_to_last_node(2, self.first)
expected = self.third
self.assertEqual(actual, expected)
def test_first_node(self):
actual = kth_to_last_node(4, self.first)
expected = self.first
self.assertEqual(actual, expected)
def test_k_greater_than_linked_list_length(self):
with self.assertRaises(Exception):
kth_to_last_node(5, self.first)
def test_k_is_zero(self):
with self.assertRaises(Exception):
kth_to_last_node(0, self.first)
unittest.main(verbosity=2)

View file

@ -0,0 +1,107 @@
import unittest
class Stack(object):
def __init__(self):
"""Initialize an empty stack"""
self.items = []
def push(self, item):
"""Push a new item onto the stack"""
self.items.append(item)
def pop(self):
"""Remove and return the last item"""
# If the stack is empty, return None
# (it would also be reasonable to throw an exception)
if not self.items:
return None
return self.items.pop()
def peek(self):
"""Return the last item without removing it"""
if not self.items:
return None
return self.items[-1]
class MaxStack(object):
# Implement the push, pop, and get_max methods
def __init__(self):
self.m = Stack()
self.stack = Stack()
def push(self, item):
if self.m.peek() is None:
self.m.push(item)
elif item >= self.m.peek():
self.m.push(item)
self.stack.push(item)
def pop(self):
x = self.stack.pop()
if x == self.m.peek():
self.m.pop()
return x
def get_max(self):
return self.m.peek()
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_stack_usage(self):
max_stack = MaxStack()
max_stack.push(5)
actual = max_stack.get_max()
expected = 5
self.assertEqual(actual, expected)
max_stack.push(4)
max_stack.push(7)
max_stack.push(7)
max_stack.push(8)
actual = max_stack.get_max()
expected = 8
self.assertEqual(actual, expected)
actual = max_stack.pop()
expected = 8
self.assertEqual(actual, expected)
actual = max_stack.get_max()
expected = 7
self.assertEqual(actual, expected)
actual = max_stack.pop()
expected = 7
self.assertEqual(actual, expected)
actual = max_stack.get_max()
expected = 7
self.assertEqual(actual, expected)
actual = max_stack.pop()
expected = 7
self.assertEqual(actual, expected)
actual = max_stack.get_max()
expected = 5
self.assertEqual(actual, expected)
actual = max_stack.pop()
expected = 4
self.assertEqual(actual, expected)
actual = max_stack.get_max()
expected = 5
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,88 @@
import unittest
################################################################################
# Solution
################################################################################
def contains_cycle(x):
if not x:
return False
elif not x.next:
return False
a = x
b = x.next
while b.next:
if a == b:
return True
a = a.next
b = b.next.next
return False
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
class LinkedListNode(object):
def __init__(self, value, next=None):
self.value = value
self.next = next
def test_linked_list_with_no_cycle(self):
fourth = Test.LinkedListNode(4)
third = Test.LinkedListNode(3, fourth)
second = Test.LinkedListNode(2, third)
first = Test.LinkedListNode(1, second)
result = contains_cycle(first)
self.assertFalse(result)
def test_cycle_loops_to_beginning(self):
fourth = Test.LinkedListNode(4)
third = Test.LinkedListNode(3, fourth)
second = Test.LinkedListNode(2, third)
first = Test.LinkedListNode(1, second)
fourth.next = first
result = contains_cycle(first)
self.assertTrue(result)
def test_cycle_loops_to_middle(self):
fifth = Test.LinkedListNode(5)
fourth = Test.LinkedListNode(4, fifth)
third = Test.LinkedListNode(3, fourth)
second = Test.LinkedListNode(2, third)
first = Test.LinkedListNode(1, second)
fifth.next = third
result = contains_cycle(first)
self.assertTrue(result)
def test_two_node_cycle_at_end(self):
fifth = Test.LinkedListNode(5)
fourth = Test.LinkedListNode(4, fifth)
third = Test.LinkedListNode(3, fourth)
second = Test.LinkedListNode(2, third)
first = Test.LinkedListNode(1, second)
fifth.next = fourth
result = contains_cycle(first)
self.assertTrue(result)
def test_empty_list(self):
result = contains_cycle(None)
self.assertFalse(result)
def test_one_element_linked_list_no_cycle(self):
first = Test.LinkedListNode(1)
result = contains_cycle(first)
self.assertFalse(result)
def test_one_element_linked_list_cycle(self):
first = Test.LinkedListNode(1)
first.next = first
result = contains_cycle(first)
self.assertTrue(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,60 @@
import time
import random
from heapq import heappush, heappop
class Memo(object):
def __init__(self, size=1):
"""
Create a key-value data-structure that will never exceed `size`
members. Memo evicts the least-recently-accessed elements from itself
before adding inserting new key-value pairs.
"""
if size <= 0:
raise Exception("We do not support an empty memo")
self.xs = {}
self.heap = [(0, None)] * size
def contains(self, k):
"""
Return true if key `k` exists in the Memo.
"""
return k in self.xs
def get(self, k):
"""
Return the memoized item at key `k`.
"""
# "touch" the element in the heap
return self.xs[k]
def set(self, k, v):
"""
Memoize value `v` at key `k`.
"""
_, to_evict = heappop(self.heap)
if to_evict != None:
del self.xs[to_evict]
heappush(self.heap, (time.time(), k))
self.xs[k] = v
memo = Memo(size=10)
def f(x):
"""
Compute some mysterious, expensive function.
"""
if memo.contains(x):
print("Hit.\t\tf({})".format(x))
return memo.get(x)
else:
print("Computing...\tf({})".format(x))
time.sleep(0.25)
res = random.randint(0, 10)
memo.set(x, res)
return res
[f(random.randint(0, 10)) for _ in range(10)]

View file

@ -0,0 +1,28 @@
# merge :: [a] -> [a] -> [a]
# merge([], []): []
# merge(xs, []): xs
# merge([], ys): ys
# merge(xs@[x|xs'], ys@[y|ys'])
# when y =< x: cons(y, merge(xs, ys'))
# when x < y: cons(x, merge(xs', ys))
def merge(xs, ys):
if xs == [] and ys == []:
return []
elif ys == []:
return xs
elif xs == []:
return ys
else:
x = xs[0]
y = ys[0]
if y <= x:
return [y] + merge(xs, ys[1:])
else:
return [x] + merge(xs[1:], ys)
print(merge([3, 4, 6, 10, 11, 15],
[1, 5, 8, 12, 14, 19]))

View file

@ -0,0 +1,94 @@
import unittest
################################################################################
# Solution
################################################################################
# do_merge_ranges :: [(Int, Int)] -> [(Int, Int)] -> [(Int, Int)]
def do_merge_ranges(prev, xs):
if len(xs) == 0:
return prev
elif len(xs) == 1:
return prev + xs
else:
a1, a2 = xs[0]
b1, b2 = xs[1]
rest = xs[2:]
if b1 <= a2:
return do_merge_ranges(prev, [(a1, max(a2, b2))] + rest)
else:
return do_merge_ranges(prev + [(a1, a2)], [(b1, b2)] + rest)
# merge_ranges :: [(Int, Int)] -> [(Int, Int)]
def merge_ranges(xs):
xs = xs[:]
xs.sort()
return do_merge_ranges([], xs)
# merge_ranges_b :: [(Int, Int)] -> [(Int, Int)]
def merge_ranges_b(xs):
fi = 0
ci = 1
result = []
xs = xs[:]
xs.sort()
while ci < len(xs):
while ci < len(xs) and xs[ci][0] <= xs[fi][1]:
xs[fi] = xs[fi][0], max(xs[ci][1], xs[fi][1])
ci += 1
result.append(xs[fi])
fi = ci
ci += 1
if fi < len(xs):
result.append(xs[fi])
return result
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_meetings_overlap(self):
actual = merge_ranges([(1, 3), (2, 4)])
expected = [(1, 4)]
self.assertEqual(actual, expected)
def test_meetings_touch(self):
actual = merge_ranges([(5, 6), (6, 8)])
expected = [(5, 8)]
self.assertEqual(actual, expected)
def test_meeting_contains_other_meeting(self):
actual = merge_ranges([(1, 8), (2, 5)])
expected = [(1, 8)]
self.assertEqual(actual, expected)
def test_meetings_stay_separate(self):
actual = merge_ranges([(1, 3), (4, 8)])
expected = [(1, 3), (4, 8)]
self.assertEqual(actual, expected)
def test_multiple_merged_meetings(self):
actual = merge_ranges([(1, 4), (2, 5), (5, 8)])
expected = [(1, 8)]
self.assertEqual(actual, expected)
def test_meetings_not_sorted(self):
actual = merge_ranges([(5, 8), (1, 4), (6, 8)])
expected = [(1, 4), (5, 8)]
self.assertEqual(actual, expected)
def test_one_long_meeting_contains_smaller_meetings(self):
actual = merge_ranges([(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)])
expected = [(1, 12)]
self.assertEqual(actual, expected)
def test_sample_input(self):
actual = merge_ranges([(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)])
expected = [(0, 1), (3, 8), (9, 12)]
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,11 @@
strict graph {
Min -- {William, Jayden, Omar}
William -- {Min, Noam}
Jayden -- {Min, Amelia, Ren, Noam}
Adam -- {Amelia, Miguel, Sofia, Lucas}
Ren -- {Jayden, Omar}
Amelia -- {Jayden, Adam, Miguel}
Miguel -- {Amelia, Adam, Liam, Nathan}
Noam -- {Nathan, Jayden, William}
Omar -- {Ren, Min, Scott}
}

View file

@ -0,0 +1,97 @@
import unittest
from collections import deque
################################################################################
# Solution
################################################################################
# get_path :: G(V, E) -> V -> V -> Maybe([V])
def get_path(g, src, dst):
q = deque()
result = None
seen = set()
q.append(([], src))
if src not in g or dst not in g:
raise Exception
while q:
p, node = q.popleft()
seen.add(node)
if node == dst:
if not result:
result = p + [node]
elif len(p + [node]) < len(result):
result = p + [node]
else:
if node not in g:
raise Exception
for x in g.get(node):
if not x in seen:
q.append((p + [node], x))
return result
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def setUp(self):
self.graph = {
'a': ['b', 'c', 'd'],
'b': ['a', 'd'],
'c': ['a', 'e'],
'd': ['a', 'b'],
'e': ['c'],
'f': ['g'],
'g': ['f'],
}
def test_two_hop_path_1(self):
actual = get_path(self.graph, 'a', 'e')
expected = ['a', 'c', 'e']
self.assertEqual(actual, expected)
def test_two_hop_path_2(self):
actual = get_path(self.graph, 'd', 'c')
expected = ['d', 'a', 'c']
self.assertEqual(actual, expected)
def test_one_hop_path_1(self):
actual = get_path(self.graph, 'a', 'c')
expected = ['a', 'c']
self.assertEqual(actual, expected)
def test_one_hop_path_2(self):
actual = get_path(self.graph, 'f', 'g')
expected = ['f', 'g']
self.assertEqual(actual, expected)
def test_one_hop_path_3(self):
actual = get_path(self.graph, 'g', 'f')
expected = ['g', 'f']
self.assertEqual(actual, expected)
def test_zero_hop_path(self):
actual = get_path(self.graph, 'a', 'a')
expected = ['a']
self.assertEqual(actual, expected)
def test_no_path(self):
actual = get_path(self.graph, 'a', 'f')
expected = None
self.assertEqual(actual, expected)
def test_start_node_not_present(self):
with self.assertRaises(Exception):
get_path(self.graph, 'h', 'a')
def test_end_node_not_present(self):
with self.assertRaises(Exception):
get_path(self.graph, 'a', 'h')
unittest.main(verbosity=2)

View file

@ -0,0 +1,78 @@
# Write a function with the following type signature:L
# equal? :: String -> String -> Bool
#
# Determine equality between two inputs with backspace characters encoded as
# "<".
################################################################################
# Solution 1
################################################################################
# from collections import deque
# def equal(a, b):
# sa = deque()
# sb = deque()
# for c in a:
# if c == '<':
# sa.pop()
# else:
# sa.append(c)
# for c in b:
# if c == '<':
# sb.pop()
# else:
# sb.append(c)
# return sa == sb
################################################################################
# Solution 2
################################################################################
def handle_dels(num_dels, i, xs):
if i < 0:
return -1
while xs[i] == '<':
num_dels += 1
i -= 1
while num_dels > 0 and xs[i] != '<':
num_dels -= 1
i -= 1
if xs[i] == '<':
return handle_dels(num_dels, i, xs)
else:
return i
def update_index(i, xs):
# TODO: Indexing into non-available parts of a string.
if xs[i] != '<' and xs[i - 1] != '<':
return i - 1
elif xs[i - 1] == '<':
return handle_dels(0, i - 1, xs)
def equal(a, b):
ia = len(a) - 1
ib = len(b) - 1
while ia >= 0 and ib >= 0:
if a[ia] != b[ib]:
return False
ia = update_index(ia, a)
ib = update_index(ib, b)
if ia != 0:
return update_index(ia, a) <= -1
if ib != 0:
return update_index(ib, b) <= -1
return True

View file

@ -0,0 +1,59 @@
import unittest
################################################################################
# Solution
################################################################################
def fib(n):
"""This should be accomplishable in O(1) space."""
if n in {0, 1}:
return n
a = 0 # i = 0
b = 1 # i = 1
for x in range(2, n + 1):
result = a + b
a = b
b = result
return result
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_zeroth_fibonacci(self):
actual = fib(0)
expected = 0
self.assertEqual(actual, expected)
def test_first_fibonacci(self):
actual = fib(1)
expected = 1
self.assertEqual(actual, expected)
def test_second_fibonacci(self):
actual = fib(2)
expected = 1
self.assertEqual(actual, expected)
def test_third_fibonacci(self):
actual = fib(3)
expected = 2
self.assertEqual(actual, expected)
def test_fifth_fibonacci(self):
actual = fib(5)
expected = 5
self.assertEqual(actual, expected)
def test_tenth_fibonacci(self):
actual = fib(10)
expected = 55
self.assertEqual(actual, expected)
def test_negative_fibonacci(self):
with self.assertRaises(Exception):
fib(-1)
unittest.main(verbosity=2)

View file

@ -0,0 +1,49 @@
from random import choice
from math import floor
# Applying Chapter 1 from "Algorithms to Live By", which describes optimal
# stopping problems. Technically this simulation is invalid because the
# `candidates` function takes a lower bound and an upper bound, which allows us
# to know the cardinal number of an individual candidates. The "look then leap"
# algorithm is ideal for no-information games - i.e. games when upper and lower
# bounds aren't known. The `look_then_leap/1` function is ignorant of this
# information, so it behaves as if in a no-information game. Strangely enough,
# this algorithm will pick the best candidate 37% of the time.
#
# Chapter 1 describes two algorithms:
# 1. Look-then-leap: ordinal numbers - i.e. no-information games. Look-then-leap
# finds the best candidate 37% of the time.
# 2. Threshold: cardinal numbers - i.e. where upper and lower bounds are
# known. The Threshold algorithm finds the best candidate ~55% of the time.
#
# All of this and more can be studied as "optimal stopping theory". This applies
# to finding a spouse, parking a car, picking an apartment in a city, and more.
# candidates :: Int -> Int -> Int -> [Int]
def candidates(lb, ub, ct):
xs = list(range(lb, ub + 1))
return [choice(xs) for _ in range(ct)]
# look_then_leap :: [Integer] -> Integer
def look_then_leap(candidates):
best = candidates[0]
seen_ct = 1
ignore_ct = floor(len(candidates) * 0.37)
for x in candidates[1:]:
if ignore_ct > 0:
ignore_ct -= 1
best = max(best, x)
else:
if x > best:
print('Choosing the {} candidate.'.format(seen_ct))
return x
seen_ct += 1
print('You may have waited too long.')
return candidates[-1]
candidates = candidates(1, 100, 100)
print(candidates)
print(look_then_leap(candidates))

View file

@ -0,0 +1,83 @@
import unittest
################################################################################
# Answer
################################################################################
class Node(object):
def __init__(self, value, children=set()):
self.value = value
self.children = children
# treeify :: Char -> Set(Char) -> Node(Char)
def treeify(x, xs):
return Node(x, [treeify(c, xs - {c}) for c in xs])
# dft :: Node(Char) -> [String]
def dft(node):
result = []
s = []
s.append(('', node))
while s:
p, n = s.pop()
p += str(n.value)
if not n.children:
result.append(p)
else:
for c in n.children:
s.append((p, c))
return result
# main :: String -> Set(String)
def get_permutations(xs):
if xs == '':
return set([''])
ys = set(xs)
trees = []
for y in ys:
trees.append(treeify(y, ys - {y}))
result = set()
for t in trees:
for d in dft(t):
result.add(d)
return result
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_empty_string(self):
actual = get_permutations('')
expected = set([''])
self.assertEqual(actual, expected)
def test_one_character_string(self):
actual = get_permutations('a')
expected = set(['a'])
self.assertEqual(actual, expected)
def test_two_character_string(self):
actual = get_permutations('ab')
expected = set(['ab', 'ba'])
self.assertEqual(actual, expected)
def test_three_character_string(self):
actual = get_permutations('abc')
expected = set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba'])
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,49 @@
from collections import Counter
import unittest
################################################################################
# Impl
################################################################################
# palindromifiable :: String -> Boolean
def has_palindrome_permutation(x):
bag = Counter(x)
odd_entries_ct = 0
for _, y in bag.items():
if y % 2 != 0:
odd_entries_ct += 1
return odd_entries_ct in {0, 1}
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_permutation_with_odd_number_of_chars(self):
result = has_palindrome_permutation('aabcbcd')
self.assertTrue(result)
def test_permutation_with_even_number_of_chars(self):
result = has_palindrome_permutation('aabccbdd')
self.assertTrue(result)
def test_no_permutation_with_odd_number_of_chars(self):
result = has_palindrome_permutation('aabcd')
self.assertFalse(result)
def test_no_permutation_with_even_number_of_chars(self):
result = has_palindrome_permutation('aabbcd')
self.assertFalse(result)
def test_empty_string(self):
result = has_palindrome_permutation('')
self.assertTrue(result)
def test_one_character_string(self):
result = has_palindrome_permutation('a')
self.assertTrue(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,55 @@
class Node(object):
# ctor :: a -> [a] -> Node(a)
def __init__(self, value, children=[]):
self.value = value
self.children = children
# is_leaf :: Node(a) -> Boolean
def is_leaf(node):
return len(node.children) == 0
# enumerate :: Node(a) -> Set(List(a))
def enumerate(node):
current = []
result = []
q = []
q.append(node)
while q:
x = q.pop()
print(x.value)
for c in x.children:
q.append(c)
current.append(x.value)
print(current)
if is_leaf(x):
result.append(current)
print("Reseting current")
current = []
return result
node = Node('root', [
Node('a', [
Node('b', [Node('c')]),
Node('c', [Node('b')]),
]),
Node('b', [
Node('a', [Node('c')]),
Node('c', [Node('a')]),
]),
Node('c', [
Node('a', [Node('b')]),
Node('b', [Node('a')]),
])
])
print('----------')
print(enumerate(node))

View file

@ -0,0 +1,9 @@
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.RandomState(10) # deterministic random data
a = np.hstack((rng.normal(size=1000), rng.normal(loc=5, scale=2, size=1000)))
_ = plt.hist(a, bins='auto') # arguments are passed to np.histogram
plt.title("Histogram with 'auto' bins")
Text(0.5, 1.0, "Histogram with 'auto' bins")
plt.show()

View file

@ -0,0 +1,68 @@
import unittest
################################################################################
# Solution
################################################################################
# f :: [Int] -> [Int]
def get_products_of_all_ints_except_at_index(xs):
if len(xs) in {0, 1}:
raise Exception
ct = len(xs)
lefts = [1] * ct
rights = [1] * ct
result = []
for i in range(1, ct):
lefts[i] = lefts[i - 1] * xs[i - 1]
for i in range(ct - 2, -1, -1):
rights[i] = rights[i + 1] * xs[i + 1]
return [lefts[i] * rights[i] for i in range(ct)]
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_small_list(self):
actual = get_products_of_all_ints_except_at_index([1, 2, 3])
expected = [6, 3, 2]
self.assertEqual(actual, expected)
def test_longer_list(self):
actual = get_products_of_all_ints_except_at_index([8, 2, 4, 3, 1, 5])
expected = [120, 480, 240, 320, 960, 192]
self.assertEqual(actual, expected)
def test_list_has_one_zero(self):
actual = get_products_of_all_ints_except_at_index([6, 2, 0, 3])
expected = [0, 0, 36, 0]
self.assertEqual(actual, expected)
def test_list_has_two_zeros(self):
actual = get_products_of_all_ints_except_at_index([4, 0, 9, 1, 0])
expected = [0, 0, 0, 0, 0]
self.assertEqual(actual, expected)
def test_one_negative_number(self):
actual = get_products_of_all_ints_except_at_index([-3, 8, 4])
expected = [32, -12, -24]
self.assertEqual(actual, expected)
def test_all_negative_numbers(self):
actual = get_products_of_all_ints_except_at_index([-7, -1, -4, -2])
expected = [-8, -56, -14, -28]
self.assertEqual(actual, expected)
def test_error_with_empty_list(self):
with self.assertRaises(Exception):
get_products_of_all_ints_except_at_index([])
def test_error_with_one_number(self):
with self.assertRaises(Exception):
get_products_of_all_ints_except_at_index([1])
unittest.main(verbosity=2)

View file

@ -0,0 +1,66 @@
import unittest
################################################################################
# Solution
################################################################################
class QueueTwoStacks(object):
def __init__(self):
self.a = []
self.b = []
def enqueue(self, x):
self.a.append(x)
def dequeue(self):
if self.b:
return self.b.pop()
else:
while self.a:
self.b.append(self.a.pop())
return self.dequeue()
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_basic_queue_operations(self):
queue = QueueTwoStacks()
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)
actual = queue.dequeue()
expected = 1
self.assertEqual(actual, expected)
actual = queue.dequeue()
expected = 2
self.assertEqual(actual, expected)
queue.enqueue(4)
actual = queue.dequeue()
expected = 3
self.assertEqual(actual, expected)
actual = queue.dequeue()
expected = 4
self.assertEqual(actual, expected)
def test_error_when_dequeue_from_new_queue(self):
queue = QueueTwoStacks()
with self.assertRaises(Exception):
queue.dequeue()
def test_error_when_dequeue_from_empty_queue(self):
queue = QueueTwoStacks()
queue.enqueue(1)
queue.enqueue(2)
actual = queue.dequeue()
expected = 1
self.assertEqual(actual, expected)
actual = queue.dequeue()
expected = 2
self.assertEqual(actual, expected)
with self.assertRaises(Exception):
queue.dequeue()
unittest.main(verbosity=2)

View file

@ -0,0 +1,246 @@
import unittest
################################################################################
# Solution
################################################################################
# bottom :: Rectangle -> Int
def bottom(x):
return x.get('bottom_y')
# top :: Rectangle -> Int
def top(x):
return bottom(x) + x.get('height')
# left :: Rectangle -> Int
def left(x):
return x.get('left_x')
# right :: Rectangle -> Int
def right(x):
return left(x) + x.get('width')
# sort_highest :: Rectangle -> Rectangle -> (Rectangle, Rectangle)
def sort_highest(x, y):
if top(x) >= top(y):
return x, y
else:
return y, x
# sort_leftmost :: Rectangle -> Rectangle -> (Rectangle, Rectangle)
def sort_leftmost(x, y):
if left(x) <= left(y):
return x, y
else:
return y, x
# rectify :: Int -> Int -> Int -> Int -> Rectify
def rectify(top=None, bottom=None, left=None, right=None):
assert top >= bottom
assert left <= right
return {
'left_x': left,
'bottom_y': bottom,
'width': right - left,
'height': top - bottom,
}
# empty_rect :: Rectangle
def empty_rect():
return {
'left_x': None,
'bottom_y': None,
'width': None,
'height': None,
}
# find_rectangular_overlap :: Rectangle -> Rectangle -> Maybe(Rectangle)
def find_rectangular_overlap(x, y):
ha, hb = sort_highest(x, y)
la, lb = sort_leftmost(x, y)
if bottom(hb) <= top(hb) <= bottom(ha) <= top(ha):
return empty_rect()
if left(la) <= right(la) <= left(lb) <= right(lb):
return empty_rect()
# We should have an intersection here.
verts = [bottom(ha), top(ha), bottom(hb), top(hb)]
verts.sort()
horzs = [left(la), right(la), left(lb), right(lb)]
horzs.sort()
return rectify(top=verts[2],
bottom=verts[1],
left=horzs[1],
right=horzs[2])
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_overlap_along_both_axes(self):
rect1 = {
'left_x': 1,
'bottom_y': 1,
'width': 6,
'height': 3,
}
rect2 = {
'left_x': 5,
'bottom_y': 2,
'width': 3,
'height': 6,
}
expected = {
'left_x': 5,
'bottom_y': 2,
'width': 2,
'height': 2,
}
actual = find_rectangular_overlap(rect1, rect2)
self.assertEqual(actual, expected)
def test_one_rectangle_inside_another(self):
rect1 = {
'left_x': 1,
'bottom_y': 1,
'width': 6,
'height': 6,
}
rect2 = {
'left_x': 3,
'bottom_y': 3,
'width': 2,
'height': 2,
}
expected = {
'left_x': 3,
'bottom_y': 3,
'width': 2,
'height': 2,
}
actual = find_rectangular_overlap(rect1, rect2)
self.assertEqual(actual, expected)
def test_both_rectangles_the_same(self):
rect1 = {
'left_x': 2,
'bottom_y': 2,
'width': 4,
'height': 4,
}
rect2 = {
'left_x': 2,
'bottom_y': 2,
'width': 4,
'height': 4,
}
expected = {
'left_x': 2,
'bottom_y': 2,
'width': 4,
'height': 4,
}
actual = find_rectangular_overlap(rect1, rect2)
self.assertEqual(actual, expected)
def test_touch_on_horizontal_edge(self):
rect1 = {
'left_x': 1,
'bottom_y': 2,
'width': 3,
'height': 4,
}
rect2 = {
'left_x': 2,
'bottom_y': 6,
'width': 2,
'height': 2,
}
expected = {
'left_x': None,
'bottom_y': None,
'width': None,
'height': None,
}
actual = find_rectangular_overlap(rect1, rect2)
self.assertEqual(actual, expected)
def test_touch_on_vertical_edge(self):
rect1 = {
'left_x': 1,
'bottom_y': 2,
'width': 3,
'height': 4,
}
rect2 = {
'left_x': 4,
'bottom_y': 3,
'width': 2,
'height': 2,
}
expected = {
'left_x': None,
'bottom_y': None,
'width': None,
'height': None,
}
actual = find_rectangular_overlap(rect1, rect2)
self.assertEqual(actual, expected)
def test_touch_at_a_corner(self):
rect1 = {
'left_x': 1,
'bottom_y': 1,
'width': 2,
'height': 2,
}
rect2 = {
'left_x': 3,
'bottom_y': 3,
'width': 2,
'height': 2,
}
expected = {
'left_x': None,
'bottom_y': None,
'width': None,
'height': None,
}
actual = find_rectangular_overlap(rect1, rect2)
self.assertEqual(actual, expected)
def test_no_overlap(self):
rect1 = {
'left_x': 1,
'bottom_y': 1,
'width': 2,
'height': 2,
}
rect2 = {
'left_x': 4,
'bottom_y': 6,
'width': 3,
'height': 6,
}
expected = {
'left_x': None,
'bottom_y': None,
'width': None,
'height': None,
}
actual = find_rectangular_overlap(rect1, rect2)
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,37 @@
import unittest
################################################################################
# Implementation
################################################################################
# get_permutations :: String -> Set(String)
def get_permutations(string):
pass
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_empty_string(self):
actual = get_permutations('')
expected = set([''])
self.assertEqual(actual, expected)
def test_one_character_string(self):
actual = get_permutations('a')
expected = set(['a'])
self.assertEqual(actual, expected)
def test_two_character_string(self):
actual = get_permutations('ab')
expected = set(['ab', 'ba'])
self.assertEqual(actual, expected)
def test_three_character_string(self):
actual = get_permutations('abc')
expected = set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba'])
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,79 @@
import unittest
################################################################################
# Solution
################################################################################
# reverse :: List(a) -> List(a)
def reverse(node):
curr = node
prev = None
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
# Make sure to understand the spec! Debugging takes time. Rewriting takes
# time.
return prev
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
class LinkedListNode(object):
def __init__(self, value, next=None):
self.value = value
self.next = next
def get_values(self):
node = self
values = []
while node is not None:
values.append(node.value)
node = node.next
return values
def test_short_linked_list(self):
second = Test.LinkedListNode(2)
first = Test.LinkedListNode(1, second)
result = reverse(first)
self.assertIsNotNone(result)
actual = result.get_values()
expected = [2, 1]
self.assertEqual(actual, expected)
def test_long_linked_list(self):
sixth = Test.LinkedListNode(6)
fifth = Test.LinkedListNode(5, sixth)
fourth = Test.LinkedListNode(4, fifth)
third = Test.LinkedListNode(3, fourth)
second = Test.LinkedListNode(2, third)
first = Test.LinkedListNode(1, second)
result = reverse(first)
self.assertIsNotNone(result)
actual = result.get_values()
expected = [6, 5, 4, 3, 2, 1]
self.assertEqual(actual, expected)
def test_one_element_linked_list(self):
first = Test.LinkedListNode(1)
result = reverse(first)
self.assertIsNotNone(result)
actual = result.get_values()
expected = [1]
self.assertEqual(actual, expected)
def test_empty_linked_list(self):
result = reverse(None)
self.assertIsNone(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,181 @@
from collections import deque
import unittest
################################################################################
# Solution
################################################################################
def rev(xs, i, j):
"""Reverse xs in place from [i, j]"""
while i < j:
xs[i], xs[j] = xs[j], xs[i]
i += 1
j -= 1
def rotate(xs, n, i=None, j=None):
"""Mutably rotates list, xs, n times. Positive n values rotate right while
negative n values rotate left. Rotate within window [i, j]."""
i = i or 0
j = j or len(xs) - 1
ct = j - i
if n < 0:
n = abs(n)
p = i + n - 1
rev(xs, i, p)
rev(xs, p + 1, j)
rev(xs, i, j)
else:
p = j - (n - 1)
rev(xs, p, j)
rev(xs, i, p - 1)
rev(xs, i, j)
return xs
def rev_words(xs, i, j):
if j + 1 == len(xs):
return 0
while j + 1 < len(xs):
while j + 1 < len(xs) and xs[j + 1] != ' ':
j += 1
rev(xs, i, j)
j += 2
i = j
return 0
def reverse_words(xs):
# first reverse everything
rev(xs, 0, len(xs) - 1)
return rev_words(xs, 0, 0)
def reverse_words_bak(xs, i=None, j=None):
i = i or 0
j = j or len(xs) - 1
w0, w1 = [], []
if i >= j:
return 0
pi = i
while pi < len(xs) and xs[pi] != ' ':
w0.append(xs[pi])
pi += 1
if pi == len(xs):
return 0
pj = j
while xs[pj] != ' ':
w1.append(xs[pj])
pj -= 1
d = len(w0) - len(w1)
rotate(xs, -1 * d, i, j)
for k in range(len(w1)):
xs[i + k] = w1[len(w1) - 1 - k]
for k in range(len(w0)):
xs[j - k] = w0[len(w0) - 1 - k]
while i != j and xs[i] != ' ' and xs[j] != ' ':
i += 1
j -= 1
if i == j:
return 0
elif xs[i] == ' ':
while j > 0 and xs[j] != ' ':
j -= 1
if j == 0:
return 0
elif xs[j] == ' ':
while i < len(xs) and xs[i] != ' ':
i += 1
if i == len(xs):
return 0
return reverse_words(xs, i + 1, j - 1)
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_rev(self):
xs = [1, 2, 3, 4, 5]
rev(xs, 0, len(xs) - 1)
self.assertEqual(xs, [5, 4, 3, 2, 1])
def test_rotate(self):
ys = [1, 2, 3, 4, 5]
xs = ys[:]
self.assertEqual(rotate(xs, 1, 1, 3), [1, 4, 2, 3, 5])
xs = ys[:]
self.assertEqual(rotate(xs, -1, 1, 3), [1, 3, 4, 2, 5])
xs = ys[:]
self.assertEqual(rotate(xs, 1), [5, 1, 2, 3, 4])
xs = ys[:]
self.assertEqual(rotate(xs, -1), [2, 3, 4, 5, 1])
xs = ys[:]
self.assertEqual(rotate(xs, -2), [3, 4, 5, 1, 2])
xs = ys[:]
self.assertEqual(rotate(xs, -5), [1, 2, 3, 4, 5])
xs = ys[:]
self.assertEqual(rotate(xs, 5), [1, 2, 3, 4, 5])
xs = ys[:]
self.assertEqual(rotate(xs, 3), [3, 4, 5, 1, 2])
def test_one_word(self):
message = list('vault')
reverse_words(message)
expected = list('vault')
self.assertEqual(message, expected)
def test_two_words(self):
message = list('thief cake')
reverse_words(message)
expected = list('cake thief')
self.assertEqual(message, expected)
def test_three_words(self):
message = list('one another get')
reverse_words(message)
expected = list('get another one')
self.assertEqual(message, expected)
def test_multiple_words_same_length(self):
message = list('rat the ate cat the')
reverse_words(message)
expected = list('the cat ate the rat')
self.assertEqual(message, expected)
def test_multiple_words_different_lengths(self):
message = list('at rat house')
reverse_words(message)
expected = list('house rat at')
self.assertEqual(message, expected)
def test_multiple_words_different_lengths(self):
message = list('yummy is cake bundt chocolate')
reverse_words(message)
expected = list('chocolate bundt cake is yummy')
self.assertEqual(message, expected)
def test_empty_string(self):
message = list('')
reverse_words(message)
expected = list('')
self.assertEqual(message, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,179 @@
import unittest
from collections import deque
################################################################################
# Implementation
################################################################################
def is_leaf(node):
return node.left is None and node.right is None
def find_largest(node):
current = node
while current.right is not None:
current = current.right
return current.value
def find_second_largest(node):
history = deque()
current = node
while current.right:
history.append(current)
current = current.right
if current.left:
return find_largest(current.left)
elif history:
return history.pop().value
else:
raise TypeError
def find_second_largest_backup(node):
history = deque()
current = node
# traverse -> largest
while current.right:
history.append(current)
current = current.right
if current.left:
return find_largest(current.left)
elif history:
return history.pop().value
else:
raise ArgumentError
# Write a iterative version to avoid consuming memory with the call stack.
# Commenting out the recursive code for now.
def find_second_largest_backup(node):
if node.left is None and node.right is None:
raise ArgumentError
elif node.right is None and is_leaf(node.left):
return node.left.value
# recursion
# elif node.right is None:
# return find_largest(node.left)
# iterative version
elif node.right is None:
current = node.left
while current.right is not None:
current = current.right
return current.value
# recursion
# TODO: Remove recursion from here.
elif not is_leaf(node.right):
return find_second_largest(node.right)
# could do an else here, but let's be more assertive.
elif is_leaf(node.right):
return node.value
else:
raise ArgumentError
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
class BinaryTreeNode(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def insert_left(self, value):
self.left = Test.BinaryTreeNode(value)
return self.left
def insert_right(self, value):
self.right = Test.BinaryTreeNode(value)
return self.right
def test_full_tree(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(30)
right = tree.insert_right(70)
left.insert_left(10)
left.insert_right(40)
right.insert_left(60)
right.insert_right(80)
actual = find_second_largest(tree)
expected = 70
self.assertEqual(actual, expected)
def test_largest_has_a_left_child(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(30)
right = tree.insert_right(70)
left.insert_left(10)
left.insert_right(40)
right.insert_left(60)
actual = find_second_largest(tree)
expected = 60
self.assertEqual(actual, expected)
def test_largest_has_a_left_subtree(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(30)
right = tree.insert_right(70)
left.insert_left(10)
left.insert_right(40)
right_left = right.insert_left(60)
right_left_left = right_left.insert_left(55)
right_left.insert_right(65)
right_left_left.insert_right(58)
actual = find_second_largest(tree)
expected = 65
self.assertEqual(actual, expected)
def test_second_largest_is_root_node(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(30)
tree.insert_right(70)
left.insert_left(10)
left.insert_right(40)
actual = find_second_largest(tree)
expected = 50
self.assertEqual(actual, expected)
def test_descending_linked_list(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(40)
left_left = left.insert_left(30)
left_left_left = left_left.insert_left(20)
left_left_left.insert_left(10)
actual = find_second_largest(tree)
expected = 40
self.assertEqual(actual, expected)
def test_ascending_linked_list(self):
tree = Test.BinaryTreeNode(50)
right = tree.insert_right(60)
right_right = right.insert_right(70)
right_right.insert_right(80)
actual = find_second_largest(tree)
expected = 70
self.assertEqual(actual, expected)
def test_error_when_tree_has_one_node(self):
tree = Test.BinaryTreeNode(50)
with self.assertRaises(Exception):
find_second_largest(tree)
def test_error_when_tree_is_empty(self):
with self.assertRaises(Exception):
find_second_largest(None)
unittest.main(verbosity=2)

View file

@ -0,0 +1,94 @@
from heapq import heappush, heappop
from collections import deque
from fixtures import weighted_graph, expanded_weights_graph
# UnweightedGraph(a) :: Map(a, Set(a))
# WeightedGraph(a) :: Map(a, Set(a))
# shortest_path_dijkstra :: Vertex -> Vertex -> WeightedGraph(Vertex)
def shortest_path_dijkstra(a, b, g):
q = []
seen = set()
heappush(q, (0, a, [a]))
while q:
w0, v0, path = heappop(q)
if v0 in seen:
continue
elif v0 == b:
return w0, path
for w1, v1 in g.get(v0):
heappush(q, (w0 + w1, v1, path + [v1]))
seen.add(v0)
return 'weighted', 'pizza'
# expand_edge :: Vertex -> (Weight, Vertex) -> Map(Vertex, [Vertex])
def expand_edge(v0, wv):
w, v1 = wv
assert w > 1
result = {v0: ['{}-{}'.format(v1, 1)]}
for x in range(w - 2):
result['{}-{}'.format(v1, x + 1)] = ['{}-{}'.format(v1, x + 2)]
result['{}-{}'.format(v1, w - 1)] = [v1]
return result
# expand_weights :: Vertex -> WeightedGraph(Vertex) -> UnweightedGraph(Vertex)
def expand_weights(v, g):
result = {}
q = deque()
seen = set()
q.append(v)
while q:
v = d.popleft()
if v in seen:
continue
x = expand_edge(v, g.get)
for w, v1 in g.get(v):
if w > 1:
ws = expand_edge(v, (w, v1))
result = {**result, **ws}
q.append(v)
pass
# shortest_path_inject :: Vertex -> Vertex -> WeightedGraph(Vertex)
def shortest_path_inject(a, b, g):
q = deque()
seen = set()
q.append((a, [a]))
while q:
v0, path = q.popleft()
if v0 == 'dummy':
continue
elif v0 in seen:
continue
elif v0 == b:
return len(path), path
for _, v1 in g.get(v0):
q.append((v1, path + [v1]))
seen.add(v0)
continue
return None, None
print(expand_edge('a', (4, 'b')))
print(expand_edge('a', (5, 'e')))
assert expand_weights('a', weighted_graph) == expanded_weights_graph
# a = 'a'
# b = 'd'
# w, x = shortest_path_dijkstra(a, b, weighted_graph)
# w1, x1 = shortest_path_inject(a, b, weighted_graph)
# print("[dijkstra] Shortest path from {} to {} is {} with weight {}".format(
# a, b, x, w))
# print("[injection] Shortest path from {} to {} is {} with weight {}".format(
# a, b, x1, w1))

View file

@ -0,0 +1,34 @@
import random
def get_random(floor, ceiling):
return random.randrange(floor, ceiling + 1)
# shuffle_in_place :: [a] -> IO ()
def shuffle_in_place(xs):
"""Fisher-Yates algorithm. Notice that shuffling here is the same as
selecting a random permutation of the input set, `xs`."""
n = len(xs) - 1
for i in range(len(xs)):
r = get_random(i, n)
xs[i], xs[r] = xs[r], xs[i]
return xs
# shuffle :: [a] -> [a]
def shuffle_not_in_place(xs):
result = []
while xs:
i = get_random(0, len(xs) - 1)
x = xs.pop(i)
result.append(x)
return result
xs = [x for x in range(9)]
print(xs)
# print(shuffle_not_in_place(xs))
print(shuffle_in_place(xs[:]))

View file

@ -0,0 +1,22 @@
# swap :: Int -> Int -> [Char] -> IO ()
def swap(ia, iz, xs):
# handle swap when ia == iz
assert ia <= iz
xs[ia], xs[iz] = xs[iz], xs[ia]
# reverse :: [Char] -> IO ()
def reverse(xs):
ia = 0
iz = len(xs) - 1
while ia <= iz:
swap(ia, iz, xs)
ia += 1
iz -= 1
x = list("superduperpooper")
reverse(x)
print(x)
print("Tests pass")

View file

@ -0,0 +1,84 @@
import unittest
################################################################################
# Solution
################################################################################
class TempTracker(object):
def __init__(self):
# min / max
self.min = None
self.max = None
# mean
self.sum = 0
self.num = 0
# mode
self.nums = [0] * 111
self.mode_num = 0
self.mode = None
def insert(self, x):
# min / max
if not self.min or x < self.min:
self.min = x
if not self.max or x > self.max:
self.max = x
# mean
self.sum += x
self.num += 1
# mode
self.nums[x] += 1
if self.nums[x] >= self.mode_num:
self.mode_num = self.nums[x]
self.mode = x
def get_max(self):
return self.max
def get_min(self):
return self.min
def get_mean(self):
return self.sum / self.num
def get_mode(self):
return self.mode
# Tests
class Test(unittest.TestCase):
def test_tracker_usage(self):
tracker = TempTracker()
tracker.insert(50)
msg = 'failed on first temp recorded'
self.assertEqual(tracker.get_max(), 50, msg='max ' + msg)
self.assertEqual(tracker.get_min(), 50, msg='min ' + msg)
self.assertEqual(tracker.get_mean(), 50.0, msg='mean ' + msg)
self.assertEqual(tracker.get_mode(), 50, msg='mode ' + msg)
tracker.insert(80)
msg = 'failed on higher temp recorded'
self.assertEqual(tracker.get_max(), 80, msg='max ' + msg)
self.assertEqual(tracker.get_min(), 50, msg='min ' + msg)
self.assertEqual(tracker.get_mean(), 65.0, msg='mean ' + msg)
self.assertIn(tracker.get_mode(), [50, 80], msg='mode ' + msg)
tracker.insert(80)
msg = 'failed on third temp recorded'
self.assertEqual(tracker.get_max(), 80, msg='max ' + msg)
self.assertEqual(tracker.get_min(), 50, msg='min ' + msg)
self.assertEqual(tracker.get_mean(), 70.0, msg='mean ' + msg)
self.assertEqual(tracker.get_mode(), 80, msg='mode ' + msg)
tracker.insert(30)
msg = 'failed on lower temp recorded'
self.assertEqual(tracker.get_max(), 80, msg='max ' + msg)
self.assertEqual(tracker.get_min(), 30, msg='min ' + msg)
self.assertEqual(tracker.get_mean(), 60.0, msg='mean ' + msg)
self.assertEqual(tracker.get_mode(), 80, msg='mode ' + msg)
unittest.main(verbosity=2)

View file

@ -0,0 +1 @@
hello

View file

@ -0,0 +1,25 @@
from collections import deque
# list:
# array:
# vector:
# bit-{array,vector}:
def sort(xs, highest):
v = [0] * (highest + 1)
result = deque()
for x in xs:
v[x] += 1
for i, x in enumerate(v):
if x > 0:
result.appendleft(i)
return list(result)
assert sort([37, 89, 41, 100, 65, 91, 53],
100) == [100, 91, 89, 65, 53, 41, 37]
print("Tests pass!")

View file

@ -0,0 +1,31 @@
from fixtures import unweighted_digraph
from collections import deque
# vertices_no_in_edges :: UnweightedDigraph -> Set(Vertex)
def vertices_no_in_edges(g):
"""Return the vertices in graph `g` with no in-edges."""
result = set()
vertices = set(g.keys())
for neighbors in g.values():
result = result.union(neighbors)
return vertices ^ result
# topo_sort :: UnweightedDigraph -> List(Vertex)
def topo_sort(g):
q = deque()
seen = set()
result = []
for x in vertices_no_in_edges(g):
q.append(x)
while q:
vertex = q.popleft()
if vertex in seen:
continue
result.append(vertex)
neighbors = g.get(vertex)
for x in g.get(vertex):
q.append(x)
seen.add(vertex)
return result
print(topo_sort(unweighted_digraph))

View file

@ -0,0 +1,38 @@
class Node(object):
def __init__(self, value, children=[]):
self.value = value
self.children = children
################################################################################
# Solution
################################################################################
def trip_time(node):
s = []
result = 0
s.append((node.value, node))
while s:
p, node = s.pop()
if not node.children:
result = max(result, p)
for x in node.children:
s.append((p + x.value, x))
return result
################################################################################
# Tests
################################################################################
tree = Node(
0,
children=[
Node(5, children=[Node(6)]),
Node(2, children=[
Node(6),
Node(10),
]),
Node(3, children=[Node(2, children=[Node(11)])]),
])
assert trip_time(tree) == 16
print("Tests pass!")

View file

@ -0,0 +1,33 @@
import unittest
################################################################################
# Solution
################################################################################
# find_repeat :: [Int] -> Int
def find_repeat(xs):
n = len(xs) - 1
return sum(xs) - ((n**2 + n) / 2)
################################################################################
# Tests
################################################################################
class Test(unittest.TestCase):
def test_short_list(self):
actual = find_repeat([1, 2, 1])
expected = 1
self.assertEqual(actual, expected)
def test_medium_list(self):
actual = find_repeat([4, 1, 3, 4, 2])
expected = 4
self.assertEqual(actual, expected)
def test_long_list(self):
actual = find_repeat([1, 5, 9, 7, 2, 6, 3, 8, 2, 4])
expected = 2
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,123 @@
import unittest
from collections import deque
def is_balanced(node):
q, seen, ds = deque(), set(), set()
q.append((0, node))
while q:
d, node = q.popleft()
l, r = node.left, node.right
seen.add(node)
if not l and not r:
if d not in ds and len(ds) == 2:
return False
else:
ds.add(d)
if l and l not in seen:
q.append((d + 1, l))
if r and r not in seen:
q.append((d + 1, r))
return max(ds) - min(ds) <= 1
# Tests
class Test(unittest.TestCase):
class BinaryTreeNode(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def insert_left(self, value):
self.left = Test.BinaryTreeNode(value)
return self.left
def insert_right(self, value):
self.right = Test.BinaryTreeNode(value)
return self.right
def test_full_tree(self):
tree = Test.BinaryTreeNode(5)
left = tree.insert_left(8)
right = tree.insert_right(6)
left.insert_left(1)
left.insert_right(2)
right.insert_left(3)
right.insert_right(4)
result = is_balanced(tree)
self.assertTrue(result)
def test_both_leaves_at_the_same_depth(self):
tree = Test.BinaryTreeNode(3)
left = tree.insert_left(4)
right = tree.insert_right(2)
left.insert_left(1)
right.insert_right(9)
result = is_balanced(tree)
self.assertTrue(result)
def test_leaf_heights_differ_by_one(self):
tree = Test.BinaryTreeNode(6)
left = tree.insert_left(1)
right = tree.insert_right(0)
right.insert_right(7)
result = is_balanced(tree)
self.assertTrue(result)
def test_leaf_heights_differ_by_two(self):
tree = Test.BinaryTreeNode(6)
left = tree.insert_left(1)
right = tree.insert_right(0)
right_right = right.insert_right(7)
right_right.insert_right(8)
result = is_balanced(tree)
self.assertFalse(result)
def test_three_leaves_total(self):
tree = Test.BinaryTreeNode(1)
left = tree.insert_left(5)
right = tree.insert_right(9)
right.insert_left(8)
right.insert_right(5)
result = is_balanced(tree)
self.assertTrue(result)
def test_both_subtrees_superbalanced(self):
tree = Test.BinaryTreeNode(1)
left = tree.insert_left(5)
right = tree.insert_right(9)
right_left = right.insert_left(8)
right.insert_right(5)
right_left.insert_left(7)
result = is_balanced(tree)
self.assertFalse(result)
def test_both_subtrees_superbalanced_two(self):
tree = Test.BinaryTreeNode(1)
left = tree.insert_left(2)
right = tree.insert_right(4)
left.insert_left(3)
left_right = left.insert_right(7)
left_right.insert_right(8)
right_right = right.insert_right(5)
right_right_right = right_right.insert_right(6)
right_right_right.insert_right(9)
result = is_balanced(tree)
self.assertFalse(result)
def test_only_one_node(self):
tree = Test.BinaryTreeNode(1)
result = is_balanced(tree)
self.assertTrue(result)
def test_linked_list_tree(self):
tree = Test.BinaryTreeNode(1)
right = tree.insert_right(2)
right_right = right.insert_right(3)
right_right.insert_right(4)
result = is_balanced(tree)
self.assertTrue(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,26 @@
# Doing a practice implementation of Dijkstra's algorithm: a priority-first
# search.
from heapq import heappush, heappop
class Node(object):
def __init__(self, value, children):
self.value = value
self.children = children
def shortest_path(a, b):
"""Return the shortest path from `a` to `b`."""
q = []
seen = set()
heappush((a.value, a, [a]), q)
while q:
d, node, path = heappop(q)
if node == b:
return path
seen.add(node)
for child in node.children:
if child not in seen:
heappush((d + child.value, child, path + [child]), q)
raise Exception("Path between nodes A and B does not exist.")

View file

@ -0,0 +1,6 @@
* Sorting
** Merge: O(n*log(n))
** Heap: O(n*log(n))
** Insertion: O(n^2)
** Quick: O(n^2)
** Bubble: O(n^2)

View file

@ -0,0 +1,55 @@
import unittest
from math import floor
def midpoint(a, b):
return a + floor((b - a) / 2)
def do_find_rotation_point(a, b, xs):
i = midpoint(a, b)
count = b - a + 1
if count == 2:
if xs[a] > xs[b]:
return b
else:
return -1
if i in {a, b}:
return i
if xs[a] < xs[i]:
return do_find_rotation_point(i, b, xs)
else:
return do_find_rotation_point(a, i, xs)
def find_rotation_point(xs):
return do_find_rotation_point(0, len(xs) - 1, xs)
# Tests
class Test(unittest.TestCase):
def test_small_list(self):
actual = find_rotation_point(['cape', 'cake'])
expected = 1
self.assertEqual(actual, expected)
def test_medium_list(self):
actual = find_rotation_point(
['grape', 'orange', 'plum', 'radish', 'apple'])
expected = 4
self.assertEqual(actual, expected)
def test_large_list(self):
actual = find_rotation_point([
'ptolemaic', 'retrograde', 'supplant', 'undulate', 'xenoepist',
'asymptote', 'babka', 'banoffee', 'engender', 'karpatka',
'othellolagkage'
])
expected = 5
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,51 @@
import unittest
def can_two_movies_fill_flight(xs, t):
seeking = set()
for x in xs:
if x in seeking:
return True
else:
seeking.add(t - x)
return False
# Tests
class Test(unittest.TestCase):
def test_short_flight(self):
result = can_two_movies_fill_flight([2, 4], 1)
self.assertFalse(result)
def test_long_flight(self):
result = can_two_movies_fill_flight([2, 4], 6)
self.assertTrue(result)
def test_one_movie_half_flight_length(self):
result = can_two_movies_fill_flight([3, 8], 6)
self.assertFalse(result)
def test_two_movies_half_flight_length(self):
result = can_two_movies_fill_flight([3, 8, 3], 6)
self.assertTrue(result)
def test_lots_of_possible_pairs(self):
result = can_two_movies_fill_flight([1, 2, 3, 4, 5, 6], 7)
self.assertTrue(result)
def test_not_using_first_movie(self):
result = can_two_movies_fill_flight([4, 3, 2], 5)
self.assertTrue(result)
def test_only_one_movie(self):
result = can_two_movies_fill_flight([6], 6)
self.assertFalse(result)
def test_no_movies(self):
result = can_two_movies_fill_flight([], 2)
self.assertFalse(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,64 @@
import unittest
def kth_to_last_node(k, x):
a, b = x, x
if k == 0:
raise Exception('Value of 0 for k is not supported')
for _ in range(k - 1):
if not a.next:
raise Exception('Value of {} for k is too large'.format(k))
a = a.next
while a.next:
a, b = a.next, b.next
return b
class Test(unittest.TestCase):
class LinkedListNode(object):
def __init__(self, value, next=None):
self.value = value
self.next = next
def get_values(self):
node = self
values = []
while node is not None:
values.append(node.value)
node = node.next
return values
def setUp(self):
self.fourth = Test.LinkedListNode(4)
self.third = Test.LinkedListNode(3, self.fourth)
self.second = Test.LinkedListNode(2, self.third)
self.first = Test.LinkedListNode(1, self.second)
def test_first_to_last_node(self):
actual = kth_to_last_node(1, self.first)
expected = self.fourth
self.assertEqual(actual, expected)
def test_second_to_last_node(self):
actual = kth_to_last_node(2, self.first)
expected = self.third
self.assertEqual(actual, expected)
def test_first_node(self):
actual = kth_to_last_node(4, self.first)
expected = self.first
self.assertEqual(actual, expected)
def test_k_greater_than_linked_list_length(self):
with self.assertRaises(Exception):
kth_to_last_node(5, self.first)
def test_k_is_zero(self):
with self.assertRaises(Exception):
kth_to_last_node(0, self.first)
unittest.main(verbosity=2)

View file

@ -0,0 +1,59 @@
import unittest
def merge_ranges(xs):
xs.sort()
result = [xs[0]]
for curr in xs[1:]:
a, z = result[-1]
if z >= curr[0]:
result[-1] = (a, max(z, curr[1]))
else:
result.append(curr)
return result
# Tests
class Test(unittest.TestCase):
def test_meetings_overlap(self):
actual = merge_ranges([(1, 3), (2, 4)])
expected = [(1, 4)]
self.assertEqual(actual, expected)
def test_meetings_touch(self):
actual = merge_ranges([(5, 6), (6, 8)])
expected = [(5, 8)]
self.assertEqual(actual, expected)
def test_meeting_contains_other_meeting(self):
actual = merge_ranges([(1, 8), (2, 5)])
expected = [(1, 8)]
self.assertEqual(actual, expected)
def test_meetings_stay_separate(self):
actual = merge_ranges([(1, 3), (4, 8)])
expected = [(1, 3), (4, 8)]
self.assertEqual(actual, expected)
def test_multiple_merged_meetings(self):
actual = merge_ranges([(1, 4), (2, 5), (5, 8)])
expected = [(1, 8)]
self.assertEqual(actual, expected)
def test_meetings_not_sorted(self):
actual = merge_ranges([(5, 8), (1, 4), (6, 8)])
expected = [(1, 4), (5, 8)]
self.assertEqual(actual, expected)
def test_one_long_meeting_contains_smaller_meetings(self):
actual = merge_ranges([(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)])
expected = [(1, 12)]
self.assertEqual(actual, expected)
def test_sample_input(self):
actual = merge_ranges([(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)])
expected = [(0, 1), (3, 8), (9, 12)]
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,56 @@
import unittest
from itertools import permutations
class Node(object):
def __init__(self, x):
self.value = x
self.children = []
def make_tree(c, xs):
root = Node(c)
for x in xs:
root.children.append(make_tree(x, xs - {x}))
return root
def get_permutations(xs):
xs = set(xs)
root = make_tree("", xs)
q, perms = [], set()
q.append(("", root))
while q:
c, node = q.pop()
if not node.children:
perms.add(c)
else:
for child in node.children:
q.append((c + child.value, child))
return perms
# Tests
class Test(unittest.TestCase):
def test_empty_string(self):
actual = get_permutations('')
expected = set([''])
self.assertEqual(actual, expected)
def test_one_character_string(self):
actual = get_permutations('a')
expected = set(['a'])
self.assertEqual(actual, expected)
def test_two_character_string(self):
actual = get_permutations('ab')
expected = set(['ab', 'ba'])
self.assertEqual(actual, expected)
def test_three_character_string(self):
actual = get_permutations('abc')
expected = set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba'])
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,74 @@
import unittest
def reverse(node):
prev = None
next = None
curr = node
while curr:
next = curr.next
curr.next = prev
prev = curr
curr = next
return prev
# Tests
class Test(unittest.TestCase):
class LinkedListNode(object):
def __init__(self, value, next=None):
self.value = value
self.next = next
def get_values(self):
node = self
values = []
while node is not None:
values.append(node.value)
node = node.next
return values
def test_short_linked_list(self):
second = Test.LinkedListNode(2)
first = Test.LinkedListNode(1, second)
result = reverse(first)
self.assertIsNotNone(result)
actual = result.get_values()
expected = [2, 1]
self.assertEqual(actual, expected)
def test_long_linked_list(self):
sixth = Test.LinkedListNode(6)
fifth = Test.LinkedListNode(5, sixth)
fourth = Test.LinkedListNode(4, fifth)
third = Test.LinkedListNode(3, fourth)
second = Test.LinkedListNode(2, third)
first = Test.LinkedListNode(1, second)
result = reverse(first)
self.assertIsNotNone(result)
actual = result.get_values()
expected = [6, 5, 4, 3, 2, 1]
self.assertEqual(actual, expected)
def test_one_element_linked_list(self):
first = Test.LinkedListNode(1)
result = reverse(first)
self.assertIsNotNone(result)
actual = result.get_values()
expected = [1]
self.assertEqual(actual, expected)
def test_empty_linked_list(self):
result = reverse(None)
self.assertIsNone(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,51 @@
def get_max_profit(xs):
best_profit = xs[1] - xs[0]
lowest_buy = xs[0]
for x in xs[1:]:
best_profit = max(best_profit, x - lowest_buy)
lowest_buy = min(lowest_buy, x)
return best_profit
# Tests
import unittest
class Test(unittest.TestCase):
def test_price_goes_up_then_down(self):
actual = get_max_profit([1, 5, 3, 2])
expected = 4
self.assertEqual(actual, expected)
def test_price_goes_down_then_up(self):
actual = get_max_profit([7, 2, 8, 9])
expected = 7
self.assertEqual(actual, expected)
def test_price_goes_up_all_day(self):
actual = get_max_profit([1, 6, 7, 9])
expected = 8
self.assertEqual(actual, expected)
def test_price_goes_down_all_day(self):
actual = get_max_profit([9, 7, 4, 1])
expected = -2
self.assertEqual(actual, expected)
def test_price_stays_the_same_all_day(self):
actual = get_max_profit([1, 1, 1, 1])
expected = 0
self.assertEqual(actual, expected)
def test_error_with_empty_prices(self):
with self.assertRaises(Exception):
get_max_profit([])
def test_error_with_one_price(self):
with self.assertRaises(Exception):
get_max_profit([1])
unittest.main(verbosity=2)

View file

@ -0,0 +1,29 @@
import unittest
def find_repeat(xs):
n = max(xs)
expected_sum = (n + 1) * n / 2
actual_sum = sum(xs)
return actual_sum - expected_sum
# Tests
class Test(unittest.TestCase):
def test_short_list(self):
actual = find_repeat([1, 2, 1])
expected = 1
self.assertEqual(actual, expected)
def test_medium_list(self):
actual = find_repeat([4, 1, 3, 4, 2])
expected = 4
self.assertEqual(actual, expected)
def test_long_list(self):
actual = find_repeat([1, 5, 9, 7, 2, 6, 3, 8, 2, 4])
expected = 2
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,2 @@
source_up
use_nix

View file

@ -0,0 +1,126 @@
import unittest
from collections import deque
# is_balanced :: Node(a) -> Bool
def is_balanced(node):
q = deque()
q.append((0, node))
mn, mx = None, None
while q:
depth, node = q.popleft()
# Current node is a leaf node
if not node.left and not node.right:
mx = depth if mx is None else max(mx, depth)
mn = depth if mn is None else min(mn, depth)
if mx - mn > 1:
return False
if node.left:
q.append((depth + 1, node.left))
if node.right:
q.append((depth + 1, node.right))
return mx - mn <= 1
# Tests
class Test(unittest.TestCase):
class BinaryTreeNode(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def insert_left(self, value):
self.left = Test.BinaryTreeNode(value)
return self.left
def insert_right(self, value):
self.right = Test.BinaryTreeNode(value)
return self.right
def test_full_tree(self):
tree = Test.BinaryTreeNode(5)
left = tree.insert_left(8)
right = tree.insert_right(6)
left.insert_left(1)
left.insert_right(2)
right.insert_left(3)
right.insert_right(4)
result = is_balanced(tree)
self.assertTrue(result)
def test_both_leaves_at_the_same_depth(self):
tree = Test.BinaryTreeNode(3)
left = tree.insert_left(4)
right = tree.insert_right(2)
left.insert_left(1)
right.insert_right(9)
result = is_balanced(tree)
self.assertTrue(result)
def test_leaf_heights_differ_by_one(self):
tree = Test.BinaryTreeNode(6)
left = tree.insert_left(1)
right = tree.insert_right(0)
right.insert_right(7)
result = is_balanced(tree)
self.assertTrue(result)
def test_leaf_heights_differ_by_two(self):
tree = Test.BinaryTreeNode(6)
left = tree.insert_left(1)
right = tree.insert_right(0)
right_right = right.insert_right(7)
right_right.insert_right(8)
result = is_balanced(tree)
self.assertFalse(result)
def test_three_leaves_total(self):
tree = Test.BinaryTreeNode(1)
left = tree.insert_left(5)
right = tree.insert_right(9)
right.insert_left(8)
right.insert_right(5)
result = is_balanced(tree)
self.assertTrue(result)
def test_both_subtrees_superbalanced(self):
tree = Test.BinaryTreeNode(1)
left = tree.insert_left(5)
right = tree.insert_right(9)
right_left = right.insert_left(8)
right.insert_right(5)
right_left.insert_left(7)
result = is_balanced(tree)
self.assertFalse(result)
def test_both_subtrees_superbalanced_two(self):
tree = Test.BinaryTreeNode(1)
left = tree.insert_left(2)
right = tree.insert_right(4)
left.insert_left(3)
left_right = left.insert_right(7)
left_right.insert_right(8)
right_right = right.insert_right(5)
right_right_right = right_right.insert_right(6)
right_right_right.insert_right(9)
result = is_balanced(tree)
self.assertFalse(result)
def test_only_one_node(self):
tree = Test.BinaryTreeNode(1)
result = is_balanced(tree)
self.assertTrue(result)
def test_linked_list_tree(self):
tree = Test.BinaryTreeNode(1)
right = tree.insert_right(2)
right_right = right.insert_right(3)
right_right.insert_right(4)
result = is_balanced(tree)
self.assertTrue(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,110 @@
import unittest
from collections import deque
# While this function solves the problem, it uses O(n) space since we're storing
# all of the less-thans and greater-thans.
def is_binary_search_tree_first_attempt(root):
q = deque()
q.append((set(), set(), root))
while q:
lts, gts, node = q.popleft()
if not all([node.value < lt for lt in lts]):
return False
if not all([node.value > gt for gt in gts]):
return False
if node.left:
q.append((lts | {node.value}, gts, node.left))
if node.right:
q.append((lts, gts | {node.value}, node.right))
return True
# While I did not originally solve this problem this way, when I learned that I
# could condense the space of my solution's runtime, I wrote this.
def is_binary_search_tree(root):
q = deque()
q.append((None, None, root))
while q:
lt, gt, node = q.popleft()
if not lt is None and node.value >= lt:
return False
if not gt is None and node.value <= gt:
return False
if node.left:
q.append((node.value, gt, node.left))
if node.right:
q.append((lt, node.value, node.right))
return True
# Tests
class Test(unittest.TestCase):
class BinaryTreeNode(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def insert_left(self, value):
self.left = Test.BinaryTreeNode(value)
return self.left
def insert_right(self, value):
self.right = Test.BinaryTreeNode(value)
return self.right
def test_valid_full_tree(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(30)
right = tree.insert_right(70)
left.insert_left(10)
left.insert_right(40)
right.insert_left(60)
right.insert_right(80)
result = is_binary_search_tree(tree)
self.assertTrue(result)
def test_both_subtrees_valid(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(30)
right = tree.insert_right(80)
left.insert_left(20)
left.insert_right(60)
right.insert_left(70)
right.insert_right(90)
result = is_binary_search_tree(tree)
self.assertFalse(result)
def test_descending_linked_list(self):
tree = Test.BinaryTreeNode(50)
left = tree.insert_left(40)
left_left = left.insert_left(30)
left_left_left = left_left.insert_left(20)
left_left_left.insert_left(10)
result = is_binary_search_tree(tree)
self.assertTrue(result)
def test_out_of_order_linked_list(self):
tree = Test.BinaryTreeNode(50)
right = tree.insert_right(70)
right_right = right.insert_right(60)
right_right.insert_right(80)
result = is_binary_search_tree(tree)
self.assertFalse(result)
def test_one_node_tree(self):
tree = Test.BinaryTreeNode(50)
result = is_binary_search_tree(tree)
self.assertTrue(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,64 @@
import unittest
# Solution
def is_first_come_first_served(xs, ys, zs):
i, j = 0, 0
for z in zs:
if i < len(xs) and z == xs[i]:
i += 1
elif j < len(ys) and z == ys[j]:
j += 1
else:
return False
return i == len(xs) and j == len(ys)
# Tests
class Test(unittest.TestCase):
def test_both_registers_have_same_number_of_orders(self):
result = is_first_come_first_served([1, 4, 5], [2, 3, 6],
[1, 2, 3, 4, 5, 6])
self.assertTrue(result)
def test_registers_have_different_lengths(self):
result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 2, 6, 3, 5])
self.assertFalse(result)
def test_one_register_is_empty(self):
result = is_first_come_first_served([], [2, 3, 6], [2, 3, 6])
self.assertTrue(result)
def test_served_orders_is_missing_orders(self):
result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 6, 3, 5])
self.assertFalse(result)
def test_served_orders_has_extra_orders(self):
result = is_first_come_first_served([1, 5], [2, 3, 6],
[1, 2, 3, 5, 6, 8])
self.assertFalse(result)
def test_one_register_has_extra_orders(self):
result = is_first_come_first_served([1, 9], [7, 8], [1, 7, 8])
self.assertFalse(result)
def test_one_register_has_unserved_orders(self):
result = is_first_come_first_served([55, 9], [7, 8], [1, 7, 8, 9])
self.assertFalse(result)
# Bonus
def test_handles_repeats(self):
actual = is_first_come_first_served([1, 2, 1], [3, 4, 5, 5],
[3, 4, 1, 5, 5, 2, 1])
self.assertTrue(actual)
def test_kitchen_didnt_serve(self):
actual = is_first_come_first_served([1, 2], [3, 4], [1, 3, 4])
self.assertFalse(actual)
def test_customer_didnt_pay(self):
actual = is_first_come_first_served([2], [3, 4], [1, 3, 4])
self.assertFalse(actual)
unittest.main(verbosity=2)

View file

@ -0,0 +1,102 @@
// The denomination of a coin.
type Coin = number;
// The amount of change remaining.
type Amount = number;
// Mapping of Coin -> Int
type CoinBag = Map<Coin, number>;
function createCoinBag(coins: Coin[]): CoinBag {
const result = new Map();
for (const coin of coins) {
result.set(coin, 0);
}
return result;
}
// This algorithm should work conceptual, but it does not actually
// work. JavaScript uses reference equality when constructing a Set<Map<A,B>>,
// so my result.size returns a higher number than I expect because it contains
// many duplicate entries.
//
// Conceptually, I'm not sure this solution is optimal either -- even after I
// can dedupe the entries in `result`.
function changePossibilities(amt: Amount, coins: Coin[]): number {
if (amt === 0) {
return 1;
}
const result: Set<CoinBag> = new Set();
const q: [Coin, Amount, CoinBag][] = [];
for (const coin of coins) {
const bag = createCoinBag(coins);
bag.set(coin, 1);
q.push([coin, amt - coin, bag]);
}
while (q.length > 0) {
const [coin, amt, bag] = q.shift();
console.log([coin, amt, bag]);
if (amt === 0) {
result.add(bag);
} else if (amt < 0) {
continue;
} else {
for (const c of coins) {
const bagCopy = new Map(bag);
const value = bagCopy.get(c);
bagCopy.set(c, value + 1);
q.push([c, amt - c, bagCopy]);
}
}
}
console.log(result);
return result.size;
}
// Tests
let desc = "sample input";
let actual = changePossibilities(4, [1, 2, 3]);
let expected = 4;
assertEqual(actual, expected, desc);
desc = "one way to make zero cents";
actual = changePossibilities(0, [1, 2]);
expected = 1;
assertEqual(actual, expected, desc);
desc = "no ways if no coins";
actual = changePossibilities(1, []);
expected = 0;
assertEqual(actual, expected, desc);
desc = "big coin value";
actual = changePossibilities(5, [25, 50]);
expected = 0;
assertEqual(actual, expected, desc);
desc = "big target amount";
actual = changePossibilities(50, [5, 10]);
expected = 6;
assertEqual(actual, expected, desc);
// I think InterviewCake designed this assertion to be computationally
// expensive.
desc = "change for one dollar";
actual = changePossibilities(100, [1, 5, 10, 25, 50]);
expected = 292;
assertEqual(actual, expected, desc);
function assertEqual(a, b, desc) {
if (a === b) {
console.log(`${desc} ... PASS`);
} else {
console.log(`${desc} ... FAIL: ${a} != ${b}`);
}
}

View file

@ -0,0 +1,57 @@
import unittest
def delete_node(node):
if node.next:
node.value = node.next.value
node.next = node.next.next
else:
raise Exception(
"We cannot delete the last node in a linked list using this function"
)
# Tests
class Test(unittest.TestCase):
class LinkedListNode(object):
def __init__(self, value, next=None):
self.value = value
self.next = next
def get_values(self):
node = self
values = []
while node is not None:
values.append(node.value)
node = node.next
return values
def setUp(self):
self.fourth = Test.LinkedListNode(4)
self.third = Test.LinkedListNode(3, self.fourth)
self.second = Test.LinkedListNode(2, self.third)
self.first = Test.LinkedListNode(1, self.second)
def test_node_at_beginning(self):
delete_node(self.first)
actual = self.first.get_values()
expected = [2, 3, 4]
self.assertEqual(actual, expected)
def test_node_in_middle(self):
delete_node(self.second)
actual = self.first.get_values()
expected = [1, 3, 4]
self.assertEqual(actual, expected)
def test_node_at_end(self):
with self.assertRaises(Exception):
delete_node(self.fourth)
def test_one_node_in_list(self):
unique = Test.LinkedListNode(1)
with self.assertRaises(Exception):
delete_node(unique)
unittest.main(verbosity=2)

View file

@ -0,0 +1,114 @@
import unittest
################################################################################
# InterviewCake's solution
################################################################################
def cycle_len(xs, i):
"""
Returns the length of a cycle that contains no duplicate items.
"""
result = 1
checkpt = i
current = xs[checkpt - 1]
while current != checkpt:
current = xs[current - 1]
result += 1
return result
def theirs(xs):
"""
This is InterviewCake's solution.
"""
i = xs[-1]
for _ in range(len(xs) - 1):
i = xs[i - 1]
cycle_length = cycle_len(xs, i)
p0 = xs[-1]
p1 = xs[-1]
for _ in range(cycle_length):
p1 = xs[p1 - 1]
while p0 != p1:
p0 = xs[p0 - 1]
p1 = xs[p1 - 1]
print(p0, p1)
return p0
################################################################################
# My solution
################################################################################
def mine(xs):
"""
This is the solution that I came up with, which differs from InterviewCake's
solution.
"""
i = xs[-1]
offset = 1 if len(xs) % 2 == 0 else 2
for _ in range(len(xs) - offset):
i = xs[i - 1]
return i
use_mine = True
find_duplicate = mine if use_mine else theirs
# Tests
class Test(unittest.TestCase):
def test_just_the_repeated_number(self):
# len(xs) even
actual = find_duplicate([1, 1])
expected = 1
self.assertEqual(actual, expected)
def test_short_list(self):
# len(xs) even
actual = find_duplicate([1, 2, 3, 2])
expected = 2
self.assertEqual(actual, expected)
def test_medium_list(self):
# len(xs) even
actual = find_duplicate([1, 2, 5, 5, 5, 5])
expected = 5
self.assertEqual(actual, expected)
def test_long_list(self):
# len(xs) odd
actual = find_duplicate([4, 1, 4, 8, 3, 2, 7, 6, 5])
expected = 4
self.assertEqual(actual, expected)
############################################################################
# Additional examples from InterviewCake.com
############################################################################
def test_example_a(self):
# len(xs) even
actual = find_duplicate([3, 4, 2, 3, 1, 5])
expected = 3
self.assertTrue(actual, expected)
def test_example_b(self):
# len(xs) even
actual = find_duplicate([3, 1, 2, 2])
expected = 2
self.assertEqual(actual, expected)
def test_example_c(self):
# len(xs) odd BUT multiple duplicates
actual = find_duplicate([4, 3, 1, 1, 4])
self.assertTrue(actual in {1, 4})
unittest.main(verbosity=2)

View file

@ -0,0 +1,70 @@
function findRepeatBruteForce(xs: Array<number>): number {
// InterviewCake asks us to write a function that optimizes for space. Using
// brute force, we can write a function that returns an answer using constant
// (i.e. O(1)) space at the cost of a quadratic (i.e. O(n^2)) runtime.
//
// I did not think of this myself; InterviewCake's "Tell me more" hints
// did. Since I think this idea is clever, I wrote a solution from memory to
// help me internalize the solution.
for (let i = 0; i < xs.length; i += 1) {
let seeking = xs[i];
for (let j = i + 1; j < xs.length; j += 1) {
if (xs[j] === seeking) {
return seeking;
}
}
}
}
function findRepeatSort(xs: Array<number>): number {
// This version first sorts xs, which gives the function a time-complexity of
// O(n*log(n)), which is better than the quadratic complexity of the
// brute-force solution. The space requirement here is constant.
//
// Since we need to sort xs in-place to avoid paying a O(n) space cost for
// storing the newly sorted xs, we're mutating our input. InterviewCake
// advises us to not mutate our input.
xs.sort();
let i = 0;
let j = 1;
for (; j < xs.length; ) {
if (xs[i] === xs[j]) {
return xs[i];
}
i += 1;
j += 1;
}
}
function findRepeat(xs: Array<number>): number {
return 0;
}
// Tests
let desc = "just the repeated number";
let actual = findRepeat([1, 1]);
let expected = 1;
assertEqual(actual, expected, desc);
desc = "short array";
actual = findRepeat([1, 2, 3, 2]);
expected = 2;
assertEqual(actual, expected, desc);
desc = "medium array";
actual = findRepeat([1, 2, 5, 5, 5, 5]);
expected = 5;
assertEqual(actual, expected, desc);
desc = "long array";
actual = findRepeat([4, 1, 4, 8, 3, 2, 7, 6, 5]);
expected = 4;
assertEqual(actual, expected, desc);
function assertEqual(a, b, desc) {
if (a === b) {
console.log(`${desc} ... PASS`);
} else {
console.log(`${desc} ... FAIL: ${a} != ${b}`);
}
}

View file

@ -0,0 +1,68 @@
function findRotationPoint(xs: Array<string>): number {
// Find the rotation point in the vector.
let beg = 0;
let end = xs.length - 1;
while (beg != end) {
let mid = beg + Math.floor((end - beg) / 2);
if (beg === mid) {
return xs[beg] < xs[end] ? beg : end;
}
if (xs[end] <= xs[mid]) {
beg = mid;
end = end;
} else {
beg = beg;
end = mid;
}
}
return beg;
}
// Tests
let desc;
let actual;
let expected;
desc = "small array one";
actual = findRotationPoint(["cape", "cake"]);
expected = 1;
assertEquals(actual, expected, desc);
desc = "small array two";
actual = findRotationPoint(["cake", "cape"]);
expected = 0;
assertEquals(actual, expected, desc);
desc = "medium array";
actual = findRotationPoint(["grape", "orange", "plum", "radish", "apple"]);
expected = 4;
assertEquals(actual, expected, desc);
desc = "large array";
actual = findRotationPoint([
"ptolemaic",
"retrograde",
"supplant",
"undulate",
"xenoepist",
"asymptote",
"babka",
"banoffee",
"engender",
"karpatka",
"othellolagkage"
]);
expected = 5;
assertEquals(actual, expected, desc);
function assertEquals(a, b, desc) {
if (a === b) {
console.log(`${desc} ... PASS`);
} else {
console.log(`${desc} ... FAIL: ${a} != ${b}`);
}
}

View file

@ -0,0 +1,232 @@
type Color = string;
interface GraphNode {
label: string;
neighbors: Set<GraphNode>;
color: string;
}
class GraphNode {
constructor(label: string) {
this.label = label;
this.neighbors = new Set();
this.color = null;
}
}
interface Queue<A> {
xs: Array<A>;
}
class Queue<A> {
constructor() {
this.xs = [];
}
isEmpty(): boolean {
return this.xs.length === 0;
}
enqueue(x: A): void {
this.xs.push(x);
}
dequeue(): A {
return this.xs.shift();
}
}
type Graph = Array<GraphNode>;
// Return a set of all of the colors from the neighbor nodes of `node`.
function neighborColors(node: GraphNode): Set<Color> {
const result: Set<Color> = new Set();
for (const x of node.neighbors) {
if (typeof x.color === 'string') {
result.add(x.color);
}
}
return result;
}
// Returns the set difference between sets `xs`, and `ys`.
function setDifference<A>(xs: Set<A>, ys: Set<A>): Set<A> {
const result: Set<A> = new Set();
for (const x of xs) {
if (!ys.has(x)) {
result.add(x);
}
}
return result;
}
// Returns an element from the set, `xs`.
// Throwns an error if `xs` is an empty set.
function choose<A>(xs: Set<A>): A {
if (xs.size === 0) {
throw new Error('Cannot choose an element from an empty set.');
} else {
return xs.values().next().value;
}
}
// Returns true if `node` is present in `node.neighbors`.
function isCyclic(node: GraphNode): boolean {
for (const x of node.neighbors) {
if (x === node) {
return true;
}
}
}
function colorGraph(graph: Graph, colors: Array<Color>): void {
const allColors = new Set(colors);
for (const node of graph) {
if (isCyclic(node)) {
throw new Error('InterviewCake would like me to invalidate this');
}
if (typeof node.color !== 'string') {
node.color = choose(setDifference(allColors, neighborColors(node)));
}
}
}
// Tests
const colors = ['red', 'green', 'blue', 'orange', 'yellow', 'white'];
let graph = [];
{
const nodeA = new GraphNode('A');
const nodeB = new GraphNode('B');
const nodeC = new GraphNode('C');
const nodeD = new GraphNode('D');
nodeA.neighbors.add(nodeB);
nodeB.neighbors.add(nodeA);
nodeB.neighbors.add(nodeC);
nodeC.neighbors.add(nodeB);
nodeC.neighbors.add(nodeD);
nodeD.neighbors.add(nodeC);
graph = [nodeA, nodeB, nodeC, nodeD];
}
colorGraph(graph, colors);
assertEqual(validateGraphColoring(graph), true, 'line graph');
{
const nodeA = new GraphNode('A');
const nodeB = new GraphNode('B');
const nodeC = new GraphNode('C');
const nodeD = new GraphNode('D');
nodeA.neighbors.add(nodeB);
nodeB.neighbors.add(nodeA);
nodeC.neighbors.add(nodeD);
nodeD.neighbors.add(nodeC);
graph = [nodeA, nodeB, nodeC, nodeD];
}
colorGraph(graph, colors);
assertEqual(validateGraphColoring(graph), true, 'separate graph');
{
const nodeA = new GraphNode('A');
const nodeB = new GraphNode('B');
const nodeC = new GraphNode('C');
nodeA.neighbors.add(nodeB);
nodeA.neighbors.add(nodeC);
nodeB.neighbors.add(nodeA);
nodeB.neighbors.add(nodeC);
nodeC.neighbors.add(nodeA);
nodeC.neighbors.add(nodeB);
graph = [nodeA, nodeB, nodeC];
}
colorGraph(graph, colors);
assertEqual(validateGraphColoring(graph), true, 'triangle graph');
{
const nodeA = new GraphNode('A');
const nodeB = new GraphNode('B');
const nodeC = new GraphNode('C');
const nodeD = new GraphNode('D');
const nodeE = new GraphNode('E');
nodeA.neighbors.add(nodeB);
nodeA.neighbors.add(nodeC);
nodeB.neighbors.add(nodeA);
nodeB.neighbors.add(nodeC);
nodeB.neighbors.add(nodeD);
nodeB.neighbors.add(nodeE);
nodeC.neighbors.add(nodeA);
nodeC.neighbors.add(nodeB);
nodeC.neighbors.add(nodeD);
nodeC.neighbors.add(nodeE);
nodeD.neighbors.add(nodeB);
nodeD.neighbors.add(nodeC);
nodeD.neighbors.add(nodeE);
nodeE.neighbors.add(nodeB);
nodeE.neighbors.add(nodeC);
nodeE.neighbors.add(nodeD);
graph = [nodeA, nodeB, nodeC, nodeD, nodeE];
}
colorGraph(graph, colors);
assertEqual(validateGraphColoring(graph), true, 'envelope graph');
{
const nodeA = new GraphNode('A');
nodeA.neighbors.add(nodeA);
graph = [nodeA];
}
assertThrows(() => {
colorGraph(graph, colors);
}, 'loop graph');
function validateGraphColoring(graph) {
const maxDegree = Math.max(...graph.map(node => node.neighbors.size));
const colorsUsed = new Set();
graph.forEach(node => {
colorsUsed.add(node.color);
});
if (colorsUsed.has(null)) {
return false;
}
if (colorsUsed.size > maxDegree + 1) {
return false;
}
let badEdges = 0;
graph.forEach(node => {
node.neighbors.forEach(neighbor => {
if (neighbor.color === node.color) {
badEdges += 1;
}
});
});
if (badEdges > 0) {
return false;
}
return true;
}
function assertEqual(a, b, desc) {
if (a === b) {
console.log(`${desc} ... PASS`);
} else {
console.log(`${desc} ... FAIL: ${a} != ${b}`);
}
}
function assertThrows(func, desc) {
try {
func();
console.log(`${desc} ... FAIL`);
} catch (e) {
console.log(`${desc} ... PASS`);
}
}

View file

@ -0,0 +1,81 @@
import unittest
import sys
import trace
def highest_product_of_3(xs):
if len(xs) < 3:
raise Exception("List needs to contain at least three elements.")
hp3 = xs[0] * xs[1] * xs[2]
hp2 = xs[0] * xs[1]
lp2 = xs[0] * xs[1]
hn = max(xs[0], xs[1])
ln = min(xs[0], xs[1])
for x in xs[2:]:
hp3 = max(hp3, hp2 * x, lp2 * x)
hp2 = max(hp2, hn * x, ln * x)
lp2 = min(lp2, hn * x, ln * x)
hn = max(hn, x)
ln = min(ln, x)
return hp3
# Tests
class Test(unittest.TestCase):
def test_short_list(self):
actual = highest_product_of_3([1, 2, 3, 4])
expected = 24
self.assertEqual(actual, expected)
def test_longer_list(self):
actual = highest_product_of_3([6, 1, 3, 5, 7, 8, 2])
expected = 336
self.assertEqual(actual, expected)
def test_list_has_one_negative(self):
actual = highest_product_of_3([-5, 4, 8, 2, 3])
expected = 96
self.assertEqual(actual, expected)
def test_list_has_two_negatives(self):
actual = highest_product_of_3([-10, 1, 3, 2, -10])
expected = 300
self.assertEqual(actual, expected)
def test_list_is_all_negatives(self):
actual = highest_product_of_3([-5, -1, -3, -2])
expected = -6
self.assertEqual(actual, expected)
def test_error_with_empty_list(self):
with self.assertRaises(Exception):
highest_product_of_3([])
def test_error_with_one_number(self):
with self.assertRaises(Exception):
highest_product_of_3([1])
def test_error_with_two_numbers(self):
with self.assertRaises(Exception):
highest_product_of_3([1, 1])
def test_custom(self):
actual = highest_product_of_3([9, 5, 2, 1, 7, 3])
expected = 9 * 7 * 5
self.assertEqual(actual, expected)
unittest.main(verbosity=2)
def main():
highest_product_of_3([-5, -1, -3, -2])
tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
trace=0,
count=1)
tracer.run('main()')
r = tracer.results()
r.write_results(show_missing=True, coverdir=".")

View file

@ -0,0 +1,85 @@
function canTwoMoviesFillFlightBonus(
xs: Array<number>,
duration: number
): boolean {
// Returns true if two movies exist that can fill the flight duration +/- 20
// minutes.
const seeking = {};
for (let x of xs) {
for (let i = 0; i < 40; i += 1) {
if (seeking[x + i + 1]) {
return true;
}
}
for (let i = 1; i <= 20; i += 1) {
seeking[duration - x - i] = true;
seeking[duration - x + i] = true;
}
}
return false;
}
function canTwoMoviesFillFlight(xs: Array<number>, duration: number): boolean {
const seeking = {};
for (let x of xs) {
if (seeking[x]) {
return true;
} else {
seeking[duration - x] = true;
}
}
return false;
}
// Tests
let desc = "short flight";
let actual = canTwoMoviesFillFlight([2, 4], 1);
let expected = false;
assertEquals(actual, expected, desc);
desc = "long flight";
actual = canTwoMoviesFillFlight([2, 4], 6);
expected = true;
assertEquals(actual, expected, desc);
desc = "one movie half flight length";
actual = canTwoMoviesFillFlight([3, 8], 6);
expected = false;
assertEquals(actual, expected, desc);
desc = "two movies half flight length";
actual = canTwoMoviesFillFlight([3, 8, 3], 6);
expected = true;
assertEquals(actual, expected, desc);
desc = "lots of possible pairs";
actual = canTwoMoviesFillFlight([1, 2, 3, 4, 5, 6], 7);
expected = true;
assertEquals(actual, expected, desc);
desc = "not using first movie";
actual = canTwoMoviesFillFlight([4, 3, 2], 5);
expected = true;
assertEquals(actual, expected, desc);
desc = "only one movie";
actual = canTwoMoviesFillFlight([6], 6);
expected = false;
assertEquals(actual, expected, desc);
desc = "no movies";
actual = canTwoMoviesFillFlight([], 2);
expected = false;
assertEquals(actual, expected, desc);
function assertEquals(a, b, desc) {
if (a === b) {
console.log(`${desc} ... PASS`);
} else {
console.log(`${desc} ... FAIL: ${a} != ${b}`);
}
}

View file

@ -0,0 +1,63 @@
function mergeArrays(xs: Array<number>, ys: Array<number>): Array<number> {
let i = 0;
let j = 0;
const result = [];
for (let q = 0; q < xs.length + ys.length; q += 1) {
if (i === xs.length) {
while (j < ys.length) {
result.push(ys[j]);
j += 1;
}
} else if (j === ys.length) {
while (i < xs.length) {
result.push(xs[i]);
i += 1;
}
} else if (xs[i] < ys[j]) {
result.push(xs[i]);
i += 1;
} else {
result.push(ys[j]);
j += 1;
}
}
return result;
}
// Tests
let desc = "both arrays are empty";
let actual = mergeArrays([], []);
let expected = [];
assertDeepEqual(actual, expected, desc);
desc = "first array is empty";
actual = mergeArrays([], [1, 2, 3]);
expected = [1, 2, 3];
assertDeepEqual(actual, expected, desc);
desc = "second array is empty";
actual = mergeArrays([5, 6, 7], []);
expected = [5, 6, 7];
assertDeepEqual(actual, expected, desc);
desc = "both arrays have some numbers";
actual = mergeArrays([2, 4, 6], [1, 3, 7]);
expected = [1, 2, 3, 4, 6, 7];
assertDeepEqual(actual, expected, desc);
desc = "arrays are different lengths";
actual = mergeArrays([2, 4, 6, 8], [1, 7]);
expected = [1, 2, 4, 6, 7, 8];
assertDeepEqual(actual, expected, desc);
function assertDeepEqual(a: Array<number>, b: Array<number>, desc: string) {
const aStr = JSON.stringify(a);
const bStr = JSON.stringify(b);
if (aStr !== bStr) {
console.log(`${desc} ... FAIL: ${aStr} != ${bStr}`);
} else {
console.log(`${desc} ... PASS`);
}
}

View file

@ -0,0 +1,115 @@
import unittest
import timeit
# Solution that uses O(n) space to store the result.
def not_in_place(xs):
xs.sort()
result = [xs[0]]
for ca, cb in xs[1:]:
pa, pb = result[-1]
if ca <= pb:
result[-1] = (pa, max(pb, cb))
else:
result.append((ca, cb))
return result
# Solution that uses O(1) space to store the result.
def in_place(xs):
xs.sort()
i = 0
j = i + 1
while j < len(xs):
pa, pb = xs[i]
ca, cb = xs[j]
if ca <= pb:
xs[i] = (pa, max(pb, cb))
del xs[j]
else:
i = j
j += 1
return xs
def test_nip():
inputs = [
[(1, 3), (2, 4)],
[(5, 6), (6, 8)],
[(1, 8), (2, 5)],
[(1, 3), (4, 8)],
[(1, 4), (2, 5), (5, 8)],
[(5, 8), (1, 4), (6, 8)],
[(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)],
[(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)],
]
for x in inputs:
not_in_place(x)
def test_ip():
inputs = [
[(1, 3), (2, 4)],
[(5, 6), (6, 8)],
[(1, 8), (2, 5)],
[(1, 3), (4, 8)],
[(1, 4), (2, 5), (5, 8)],
[(5, 8), (1, 4), (6, 8)],
[(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)],
[(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)],
]
for x in inputs:
in_place(x)
merge_ranges = in_place
setup = 'from __main__ import test_nip, test_ip'
print(timeit.timeit('test_nip()', number=10000, setup=setup))
print(timeit.timeit('test_ip()', number=10000, setup=setup))
# Tests
class Test(unittest.TestCase):
def test_meetings_overlap(self):
actual = merge_ranges([(1, 3), (2, 4)])
expected = [(1, 4)]
self.assertEqual(actual, expected)
def test_meetings_touch(self):
actual = merge_ranges([(5, 6), (6, 8)])
expected = [(5, 8)]
self.assertEqual(actual, expected)
def test_meeting_contains_other_meeting(self):
actual = merge_ranges([(1, 8), (2, 5)])
expected = [(1, 8)]
self.assertEqual(actual, expected)
def test_meetings_stay_separate(self):
actual = merge_ranges([(1, 3), (4, 8)])
expected = [(1, 3), (4, 8)]
self.assertEqual(actual, expected)
def test_multiple_merged_meetings(self):
actual = merge_ranges([(1, 4), (2, 5), (5, 8)])
expected = [(1, 8)]
self.assertEqual(actual, expected)
def test_meetings_not_sorted(self):
actual = merge_ranges([(5, 8), (1, 4), (6, 8)])
expected = [(1, 4), (5, 8)]
self.assertEqual(actual, expected)
def test_one_long_meeting_contains_smaller_meetings(self):
actual = merge_ranges([(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)])
expected = [(1, 12)]
self.assertEqual(actual, expected)
def test_sample_input(self):
actual = merge_ranges([(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)])
expected = [(0, 1), (3, 8), (9, 12)]
self.assertEqual(actual, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,183 @@
import unittest
from collections import deque
from heapq import heappush, heappop
################################################################################
# InterviewCake.com
################################################################################
# construct_path :: Map String String -> String -> String -> [String]
def construct_path(paths, beg, end):
"""
Reconstruct the path from `beg` to `end`.
"""
result = []
current = end
print(paths)
print(beg, end)
print('-----')
while current:
result.append(current)
current = paths[current]
result.reverse()
return result
def get_path_ic(graph, beg, end):
"""
InterviewCake uses a dictionary and back-tracking to store and reconstruct
the path instead of storing the path as state on each node.
This reduces the memory costs. See get_path_bft for an example of this less
optimal solution.
"""
if beg not in graph:
raise Exception('Origin node absent from graph.')
if end not in graph:
raise Exception('Destination node absent from graph.')
q = deque()
q.append(beg)
paths = {beg: None}
while q:
node = q.popleft()
if node == end:
print(graph)
return construct_path(paths, beg, end)
for x in graph[node]:
if x not in paths:
paths[x] = node
q.append(x)
return None
################################################################################
# Per-node state
################################################################################
def get_path_bft(graph, beg, end):
"""
Here we find the shortest path from `beg` to `end` in `graph` by doing a BFT
from beg to end and storing the path state alongside each node in the queue.
"""
if beg not in graph:
raise Exception('Origin node absent from graph.')
if end not in graph:
raise Exception('Destination node absent from graph.')
q = deque()
seen = set()
q.append([beg])
while q:
path = q.popleft()
node = path[-1]
seen.add(node)
if node == end:
return path
for x in graph[node]:
if x not in seen:
q.append(path + [x])
################################################################################
# Dijkstra's Algorithm
################################################################################
def get_path(graph, beg, end):
"""
Here we find the shortest path using Dijkstra's algorithm, which is my
favorite solution.
"""
if beg not in graph:
raise Exception(
'The origin node, {}, is not present in the graph'.format(beg))
if end not in graph:
raise Exception(
'The origin node, {}, is not present in the graph'.format(end))
q = []
seen = set()
heappush(q, (1, [beg]))
while q:
weight, path = heappop(q)
node = path[-1]
seen.add(node)
if node == end:
return path
for x in graph[node]:
if x not in seen:
heappush(q, (weight + 1, path + [x]))
return None
# Tests
class Test(unittest.TestCase):
def setUp(self):
self.graph = {
'a': ['b', 'c', 'd'],
'b': ['a', 'd'],
'c': ['a', 'e'],
'd': ['b', 'a'],
'e': ['c'],
'f': ['g'],
'g': ['f'],
}
def test_two_hop_path_1(self):
actual = get_path(self.graph, 'a', 'e')
expected = ['a', 'c', 'e']
self.assertEqual(actual, expected)
def test_two_hop_path_2(self):
actual = get_path(self.graph, 'd', 'c')
expected = ['d', 'a', 'c']
self.assertEqual(actual, expected)
def test_one_hop_path_1(self):
actual = get_path(self.graph, 'a', 'c')
expected = ['a', 'c']
self.assertEqual(actual, expected)
def test_one_hop_path_2(self):
actual = get_path(self.graph, 'f', 'g')
expected = ['f', 'g']
self.assertEqual(actual, expected)
def test_one_hop_path_3(self):
actual = get_path(self.graph, 'g', 'f')
expected = ['g', 'f']
self.assertEqual(actual, expected)
def test_zero_hop_path(self):
actual = get_path(self.graph, 'a', 'a')
expected = ['a']
self.assertEqual(actual, expected)
def test_no_path(self):
actual = get_path(self.graph, 'a', 'f')
expected = None
self.assertEqual(actual, expected)
def test_start_node_not_present(self):
with self.assertRaises(Exception):
get_path(self.graph, 'h', 'a')
def test_end_node_not_present(self):
with self.assertRaises(Exception):
get_path(self.graph, 'a', 'h')
unittest.main(verbosity=2)

View file

@ -0,0 +1,104 @@
# Herein I'm practicing two-dimensional matrix traversals in all directions of
# which I can conceive:
# 0. T -> B; L -> R
# 1. T -> B; R -> L
# 2. B -> T; L -> R
# 3. B -> T; R -> L
#
# Commentary:
# When I think of matrices, I'm reminded of cartesian planes. I think of the
# cells as (X,Y) coordinates. This has been a pitfall for me because matrices
# are usually encoded in the opposite way. That is, to access a cell at the
# coordinates (X,Y) given a matrix M, you index M like this: M[Y][X]. To attempt
# to avoid this confusion, instead of saying X and Y, I will prefer saying
# "column" and "row".
#
# When traversing a matrix, you typically traverse vertically and then
# horizontally; in other words, the rows come first followed by the columns. As
# such, I'd like to refer to traversal orders as "top-to-bottom, left-to-right"
# rather than "left-to-right, top-to-bottom".
#
# These practices are all in an attempt to rewire my thinking.
# This is a list of matrices where the index of a matrix corresponds to the
# order in which it should be traversed to produce the sequence:
# [1,2,3,4,5,6,7,8,9].
boards = [[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[3, 2, 1], [6, 5, 4], [9, 8, 7]],
[[7, 8, 9], [4, 5, 6], [1, 2, 3]], [[9, 8, 7], [6, 5, 4], [3, 2, 1]]]
# T -> B; L -> R
board = boards[0]
result = []
for row in board:
for col in row:
result.append(col)
print(result)
# T -> B; R -> L
board = boards[1]
result = []
for row in board:
for col in reversed(row):
result.append(col)
print(result)
# B -> T; L -> R
board = boards[2]
result = []
for row in reversed(board):
for col in row:
result.append(col)
print(result)
# B -> T; R -> L
board = boards[3]
result = []
for row in reversed(board):
for col in reversed(row):
result.append(col)
print(result)
################################################################################
# Neighbors
################################################################################
import random
# Generate a matrix of size `rows` x `cols` where each cell contains an item
# randomly selected from `xs`.
def generate_board(rows, cols, xs):
result = []
for _ in range(rows):
row = []
for _ in range(cols):
row.append(random.choice(xs))
result.append(row)
return result
# Print the `board` to the screen.
def print_board(board):
print('\n'.join([' '.join(row) for row in board]))
board = generate_board(4, 5, ['R', 'G', 'B'])
print_board(board)
# Return all of the cells horizontally and vertically accessible from a starting
# cell at `row`, `col` in `board`.
def neighbors(row, col, board):
result = {'top': [], 'bottom': [], 'left': [], 'right': []}
for i in range(row - 1, -1, -1):
result['top'].append(board[i][col])
for i in range(row + 1, len(board)):
result['bottom'].append(board[i][col])
for i in range(col - 1, -1, -1):
result['left'].append(board[row][i])
for i in range(col + 1, len(board[0])):
result['right'].append(board[row][i])
return result
print(neighbors(1, 2, board))

View file

@ -0,0 +1,72 @@
import unittest
# Compute the fibonacci using a bottom-up algorithm.
def fib(n):
if n < 0:
raise Error('Cannot call fibonacci with negative values')
cache = [0, 1]
for i in range(n):
cache[0], cache[1] = cache[1], cache[0] + cache[1]
return cache[0]
# Compute the fibonacci using memoization.
def fib_memoized(n):
cache = {
0: 0,
1: 1,
}
def do_fib(n):
if n < 0:
raise Error('The fib function does not support negative inputs')
if n in cache:
return cache[n]
cache[n - 1] = do_fib(n - 1)
cache[n - 2] = do_fib(n - 2)
return cache[n - 1] + cache[n - 2]
return do_fib(n)
# Tests
class Test(unittest.TestCase):
def test_zeroth_fibonacci(self):
actual = fib(0)
expected = 0
self.assertEqual(actual, expected)
def test_first_fibonacci(self):
actual = fib(1)
expected = 1
self.assertEqual(actual, expected)
def test_second_fibonacci(self):
actual = fib(2)
expected = 1
self.assertEqual(actual, expected)
def test_third_fibonacci(self):
actual = fib(3)
expected = 2
self.assertEqual(actual, expected)
def test_fifth_fibonacci(self):
actual = fib(5)
expected = 5
self.assertEqual(actual, expected)
def test_tenth_fibonacci(self):
actual = fib(10)
expected = 55
self.assertEqual(actual, expected)
def test_negative_fibonacci(self):
with self.assertRaises(Exception):
fib(-1)
unittest.main(verbosity=2)

View file

@ -0,0 +1,79 @@
{
"name": "deepmind-part-two",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
"make-error": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
"dev": true
},
"prettier": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.2.tgz",
"integrity": "sha512-5xJQIPT8BraI7ZnaDwSbu5zLrB6vvi8hVV58yHQ+QK64qrY40dULy0HSRlQ2/2IdzeBpjhDkqdcFBnFeDEMVdg==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"source-map-support": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
"integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"ts-node": {
"version": "8.6.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz",
"integrity": "sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==",
"dev": true,
"requires": {
"arg": "^4.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.6",
"yn": "3.1.1"
}
},
"typescript": {
"version": "3.7.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
"dev": true
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
}
}
}

View file

@ -0,0 +1,16 @@
{
"name": "deepmind-part-two",
"version": "1.0.0",
"description": "Practicing coding interview questions",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "William Carroll",
"license": "MIT",
"devDependencies": {
"prettier": "^2.0.2",
"ts-node": "^8.6.2",
"typescript": "^3.7.5"
}
}

View file

@ -0,0 +1,37 @@
import unittest
from collections import Counter
def has_palindrome_permutation(xs):
vs = Counter(xs).values()
return len([v for v in vs if v % 2 == 1]) in {0, 1}
# Tests
class Test(unittest.TestCase):
def test_permutation_with_odd_number_of_chars(self):
result = has_palindrome_permutation('aabcbcd')
self.assertTrue(result)
def test_permutation_with_even_number_of_chars(self):
result = has_palindrome_permutation('aabccbdd')
self.assertTrue(result)
def test_no_permutation_with_odd_number_of_chars(self):
result = has_palindrome_permutation('aabcd')
self.assertFalse(result)
def test_no_permutation_with_even_number_of_chars(self):
result = has_palindrome_permutation('aabbcd')
self.assertFalse(result)
def test_empty_string(self):
result = has_palindrome_permutation('')
self.assertTrue(result)
def test_one_character_string(self):
result = has_palindrome_permutation('a')
self.assertTrue(result)
unittest.main(verbosity=2)

View file

@ -0,0 +1,68 @@
import unittest
# get_products_of_all_ints_except_at_index :: [Int] -> [Int]
def get_products_of_all_ints_except_at_index(xs):
n = len(xs)
if n < 2:
raise Exception("Cannot computer without 2 or elements")
# lhs
befores = [None] * n
befores[0] = 1
for i in range(1, n):
befores[i] = befores[i - 1] * xs[i - 1]
# rhs
afters = [None] * n
afters[-1] = 1
for i in range(n - 2, -1, -1):
afters[i] = afters[i + 1] * xs[i + 1]
result = [None] * n
for i in range(n):
result[i] = befores[i] * afters[i]
return result
# Tests
class Test(unittest.TestCase):
def test_small_list(self):
actual = get_products_of_all_ints_except_at_index([1, 2, 3])
expected = [6, 3, 2]
self.assertEqual(actual, expected)
def test_longer_list(self):
actual = get_products_of_all_ints_except_at_index([8, 2, 4, 3, 1, 5])
expected = [120, 480, 240, 320, 960, 192]
self.assertEqual(actual, expected)
def test_list_has_one_zero(self):
actual = get_products_of_all_ints_except_at_index([6, 2, 0, 3])
expected = [0, 0, 36, 0]
self.assertEqual(actual, expected)
def test_list_has_two_zeros(self):
actual = get_products_of_all_ints_except_at_index([4, 0, 9, 1, 0])
expected = [0, 0, 0, 0, 0]
self.assertEqual(actual, expected)
def test_one_negative_number(self):
actual = get_products_of_all_ints_except_at_index([-3, 8, 4])
expected = [32, -12, -24]
self.assertEqual(actual, expected)
def test_all_negative_numbers(self):
actual = get_products_of_all_ints_except_at_index([-7, -1, -4, -2])
expected = [-8, -56, -14, -28]
self.assertEqual(actual, expected)
def test_error_with_empty_list(self):
with self.assertRaises(Exception):
get_products_of_all_ints_except_at_index([])
def test_error_with_one_number(self):
with self.assertRaises(Exception):
get_products_of_all_ints_except_at_index([1])
unittest.main(verbosity=2)

View file

@ -0,0 +1,85 @@
// Returns a new string comprised of every characters in `xs` except for the
// character at `i`.
function everyOtherChar(xs: string, i: number): string[] {
const result = [];
for (let j = 0; j < xs.length; j += 1) {
if (i !== j) {
result.push(xs[j]);
}
}
return [xs[i], result.join('')];
}
function getPermutations(xs: string): Set<string> {
if (xs === '') {
return new Set(['']);
}
const result: Set<string> = new Set;
for (let i = 0; i < xs.length; i += 1) {
const [char, rest] = everyOtherChar(xs, i);
const perms = getPermutations(rest);
for (const perm of perms) {
result.add(char + perm);
}
}
return result;
}
// Tests
let desc = 'empty string';
let input = '';
let actual = getPermutations(input);
let expected = new Set(['']);
assert(isSetsEqual(actual, expected), desc);
desc = 'one character string';
input = 'a';
actual = getPermutations(input);
expected = new Set(['a']);
assert(isSetsEqual(actual, expected), desc);
desc = 'two character string';
input = 'ab';
actual = getPermutations(input);
expected = new Set(['ab', 'ba']);
assert(isSetsEqual(actual, expected), desc);
desc = 'three character string';
input = 'abc';
actual = getPermutations(input);
expected = new Set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba']);
assert(isSetsEqual(actual, expected), desc);
desc = 'four character string';
input = 'abca';
actual = getPermutations(input);
expected = new Set([
'abca', 'abac', 'acba', 'acab', 'aabc', 'aacb', 'baca', 'baac', 'bcaa',
'bcaa', 'baac', 'baca', 'caba', 'caab', 'cbaa', 'cbaa', 'caab', 'caba',
'aabc', 'aacb', 'abac', 'abca', 'acab', 'acba'
]);
assert(isSetsEqual(actual, expected), desc);
function isSetsEqual(as, bs) {
if (as.size !== bs.size) {
return false;
}
for (let a of as) {
if (!bs.has(a)) return false;
}
return true;
}
function assert(condition, desc) {
if (condition) {
console.log(`${desc} ... PASS`);
} else {
console.log(`${desc} ... FAIL`);
}
}

View file

@ -0,0 +1,13 @@
// Reverse array of characters, `xs`, mutatively.
function reverse(xs: Array<string>) {
let i: number = 0;
let j: number = xs.length - 1;
while (i < j) {
let tmp = xs[i];
xs[i] = xs[j]
xs[j] = tmp
i += 1
j -= 1
}
}

View file

@ -0,0 +1,74 @@
import unittest
def reverse(xs, i, j):
"""Reverse array of characters, xs, in-place."""
while i < j:
xs[i], xs[j] = xs[j], xs[i]
i += 1
j -= 1
def reverse_words(xs):
punctuation = None
if len(xs) > 0 and xs[-1] in ".?!":
punctuation = xs.pop()
reverse(xs, 0, len(xs) - 1)
i = 0
j = i
while j < len(xs):
while j < len(xs) and xs[j] != ' ':
j += 1
reverse(xs, i, j - 1)
j += 1
i = j
if punctuation:
xs.append(punctuation)
# Tests
class Test(unittest.TestCase):
def test_one_word(self):
message = list('vault')
reverse_words(message)
expected = list('vault')
self.assertEqual(message, expected)
def test_two_words(self):
message = list('thief cake')
reverse_words(message)
expected = list('cake thief')
self.assertEqual(message, expected)
def test_three_words(self):
message = list('one another get')
reverse_words(message)
expected = list('get another one')
self.assertEqual(message, expected)
def test_multiple_words_same_length(self):
message = list('rat the ate cat the')
reverse_words(message)
expected = list('the cat ate the rat')
self.assertEqual(message, expected)
def test_multiple_words_different_lengths(self):
message = list('yummy is cake bundt chocolate')
reverse_words(message)
expected = list('chocolate bundt cake is yummy')
self.assertEqual(message, expected)
def test_empty_string(self):
message = list('')
reverse_words(message)
expected = list('')
self.assertEqual(message, expected)
def test_bonus_support_punctuation(self):
message = list('yummy is cake bundt chocolate this!')
reverse_words(message)
expected = list('this chocolate bundt cake is yummy!')
self.assertEqual(message, expected)
unittest.main(verbosity=2)

View file

@ -0,0 +1,219 @@
/*******************************************************************************
* Setup
******************************************************************************/
interface BinaryTreeNode {
value: number;
left: BinaryTreeNode;
right: BinaryTreeNode;
}
class BinaryTreeNode {
constructor(value: number) {
this.value = value;
this.left = null;
this.right = null;
}
insertLeft(value: number): BinaryTreeNode {
this.left = new BinaryTreeNode(value);
return this.left;
}
insertRight(value: number): BinaryTreeNode {
this.right = new BinaryTreeNode(value);
return this.right;
}
}
/*******************************************************************************
* First solution
******************************************************************************/
/**
* I first solved this problem using O(n) space and O(n*log(n))
* time. InterviewCake informs me that we can improve both the time and the
* space performance.
*/
function findSecondLargest_first(node: BinaryTreeNode): number {
const stack: Array<BinaryTreeNode> = [];
const xs: Array<number> = [];
stack.push(node);
while (stack.length > 0) {
const node = stack.pop()
xs.push(node.value);
if (node.left) {
stack.push(node.left);
}
if (node.right) {
stack.push(node.right);
}
}
xs.sort();
if (xs.length < 2) {
throw new Error('Cannot find the second largest element in a BST with fewer than two elements.');
} else {
return xs[xs.length - 2];
}
}
/*******************************************************************************
* Second solution
******************************************************************************/
/**
* My second solution accumulates a list of the values in the tree using an
* in-order traversal. This reduces the runtime costs from O(n*log(n)) from the
* previous solution to O(n). The memory cost is still O(n), which InterviewCake
* informs me can be reduced to O(1).
*/
function findSecondLargest_second(node: BinaryTreeNode): number {
const xs: Array<number> = accumulateInorder(node);
if (xs.length < 2) {
throw new Error('Cannot find the second largest element in a BST with fewer than two elements.');
} else {
return xs[xs.length - 2];
}
}
/**
* Returns an array containing the values of the tree, `node`, sorted in-order
* (i.e. from smallest-to-largest).
*/
function accumulateInorder(node: BinaryTreeNode): Array<number> {
let result = [];
if (node.left) {
result = result.concat(accumulateInorder(node.left));
}
result.push(node.value)
if (node.right) {
result = result.concat(accumulateInorder(node.right));
}
return result;
}
/*******************************************************************************
* Third solution
******************************************************************************/
/**
* Returns the largest number in a BST.
*/
function findLargest(node: BinaryTreeNode): number {
let curr: BinaryTreeNode = node;
while (curr.right) {
curr = curr.right;
}
return curr.value;
}
/**
* Returns the second largest number in a BST
*/
function findSecondLargest(node: BinaryTreeNode): number {
let curr = node;
let parent = null;
while (curr.right) {
parent = curr;
curr = curr.right
}
if (curr.left) {
return findLargest(curr.left);
}
else {
return parent.value;
}
}
// Tests
let desc = 'full tree';
let treeRoot = new BinaryTreeNode(50);
let leftNode = treeRoot.insertLeft(30);
leftNode.insertLeft(10);
leftNode.insertRight(40);
let rightNode = treeRoot.insertRight(70);
rightNode.insertLeft(60);
rightNode.insertRight(80);
assertEquals(findSecondLargest(treeRoot), 70, desc);
desc = 'largest has a left child';
treeRoot = new BinaryTreeNode(50);
leftNode = treeRoot.insertLeft(30);
leftNode.insertLeft(10);
leftNode.insertRight(40);
rightNode = treeRoot.insertRight(70);
rightNode.insertLeft(60);
assertEquals(findSecondLargest(treeRoot), 60, desc);
desc = 'largest has a left subtree';
treeRoot = new BinaryTreeNode(50);
leftNode = treeRoot.insertLeft(30);
leftNode.insertLeft(10);
leftNode.insertRight(40);
rightNode = treeRoot.insertRight(70);
leftNode = rightNode.insertLeft(60);
leftNode.insertRight(65);
leftNode = leftNode.insertLeft(55);
leftNode.insertRight(58);
assertEquals(findSecondLargest(treeRoot), 65, desc);
desc = 'second largest is root node';
treeRoot = new BinaryTreeNode(50);
leftNode = treeRoot.insertLeft(30);
leftNode.insertLeft(10);
leftNode.insertRight(40);
rightNode = treeRoot.insertRight(70);
assertEquals(findSecondLargest(treeRoot), 50, desc);
desc = 'descending linked list';
treeRoot = new BinaryTreeNode(50);
leftNode = treeRoot.insertLeft(40);
leftNode = leftNode.insertLeft(30);
leftNode = leftNode.insertLeft(20);
leftNode = leftNode.insertLeft(10);
assertEquals(findSecondLargest(treeRoot), 40, desc);
desc = 'ascending linked list';
treeRoot = new BinaryTreeNode(50);
rightNode = treeRoot.insertRight(60);
rightNode = rightNode.insertRight(70);
rightNode = rightNode.insertRight(80);
assertEquals(findSecondLargest(treeRoot), 70, desc);
desc = 'one node tree';
treeRoot = new BinaryTreeNode(50);
assertThrowsError(() => findSecondLargest(treeRoot), desc);
desc = 'when tree is empty';
treeRoot = null;
assertThrowsError(() => findSecondLargest(treeRoot), desc);
function assertEquals(a, b, desc) {
if (a === b) {
console.log(`${desc} ... PASS`);
} else {
console.log(`${desc} ... FAIL: ${a} != ${b}`)
}
}
function assertThrowsError(func, desc) {
try {
func();
console.log(`${desc} ... FAIL`);
} catch (e) {
console.log(`${desc} ... PASS`);
}
}

Some files were not shown because too many files have changed in this diff Show more