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:
commit
019f8fd211
766 changed files with 175420 additions and 0 deletions
6
users/wpcarro/scratch/README.md
Normal file
6
users/wpcarro/scratch/README.md
Normal 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.
|
4
users/wpcarro/scratch/advent-of-code-2019/README.md
Normal file
4
users/wpcarro/scratch/advent-of-code-2019/README.md
Normal 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.
|
119
users/wpcarro/scratch/advent-of-code-2019/day_1.py
Normal file
119
users/wpcarro/scratch/advent-of-code-2019/day_1.py
Normal 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))
|
32
users/wpcarro/scratch/advent-of-code-2019/day_2.py
Normal file
32
users/wpcarro/scratch/advent-of-code-2019/day_2.py
Normal 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)
|
137
users/wpcarro/scratch/advent-of-code-2019/day_3.py
Normal file
137
users/wpcarro/scratch/advent-of-code-2019/day_3.py
Normal 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))
|
35
users/wpcarro/scratch/advent-of-code-2019/day_4.py
Normal file
35
users/wpcarro/scratch/advent-of-code-2019/day_4.py
Normal 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)]))
|
170
users/wpcarro/scratch/advent-of-code-2019/day_5.py
Normal file
170
users/wpcarro/scratch/advent-of-code-2019/day_5.py
Normal 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))
|
155
users/wpcarro/scratch/advent-of-code-2019/day_6.py
Normal file
155
users/wpcarro/scratch/advent-of-code-2019/day_6.py
Normal 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))
|
49
users/wpcarro/scratch/advent-of-code-2019/day_7.py
Normal file
49
users/wpcarro/scratch/advent-of-code-2019/day_7.py
Normal 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)
|
40
users/wpcarro/scratch/crack_the_coding_interview/11_1.py
Normal file
40
users/wpcarro/scratch/crack_the_coding_interview/11_1.py
Normal 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!')
|
11
users/wpcarro/scratch/crack_the_coding_interview/to_tree.hs
Normal file
11
users/wpcarro/scratch/crack_the_coding_interview/to_tree.hs
Normal 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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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))
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
65
users/wpcarro/scratch/data_structures_and_algorithms/dft.py
Normal file
65
users/wpcarro/scratch/data_structures_and_algorithms/dft.py
Normal 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()
|
|
@ -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))
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
110
users/wpcarro/scratch/data_structures_and_algorithms/fixtures.py
Normal file
110
users/wpcarro/scratch/data_structures_and_algorithms/fixtures.py
Normal 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.
|
|
@ -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)
|
|
@ -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!')
|
|
@ -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)
|
|
@ -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")
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
60
users/wpcarro/scratch/data_structures_and_algorithms/memo.py
Normal file
60
users/wpcarro/scratch/data_structures_and_algorithms/memo.py
Normal 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)]
|
|
@ -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]))
|
|
@ -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)
|
|
@ -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}
|
||||
}
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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))
|
|
@ -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)
|
|
@ -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)
|
|
@ -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))
|
|
@ -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()
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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))
|
|
@ -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[:]))
|
|
@ -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")
|
|
@ -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)
|
|
@ -0,0 +1 @@
|
|||
hello
|
|
@ -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!")
|
|
@ -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))
|
|
@ -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!")
|
|
@ -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)
|
123
users/wpcarro/scratch/deepmind/part_one/balanced-binary-tree.py
Normal file
123
users/wpcarro/scratch/deepmind/part_one/balanced-binary-tree.py
Normal 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)
|
26
users/wpcarro/scratch/deepmind/part_one/dijkstra.py
Normal file
26
users/wpcarro/scratch/deepmind/part_one/dijkstra.py
Normal 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.")
|
6
users/wpcarro/scratch/deepmind/part_one/efficiency.org
Normal file
6
users/wpcarro/scratch/deepmind/part_one/efficiency.org
Normal 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)
|
|
@ -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)
|
|
@ -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)
|
64
users/wpcarro/scratch/deepmind/part_one/kth-to-last.py
Normal file
64
users/wpcarro/scratch/deepmind/part_one/kth-to-last.py
Normal 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)
|
59
users/wpcarro/scratch/deepmind/part_one/merging-ranges.py
Normal file
59
users/wpcarro/scratch/deepmind/part_one/merging-ranges.py
Normal 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)
|
|
@ -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)
|
|
@ -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)
|
51
users/wpcarro/scratch/deepmind/part_one/stock-price.py
Normal file
51
users/wpcarro/scratch/deepmind/part_one/stock-price.py
Normal 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)
|
|
@ -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)
|
2
users/wpcarro/scratch/deepmind/part_two/.envrc
Normal file
2
users/wpcarro/scratch/deepmind/part_two/.envrc
Normal file
|
@ -0,0 +1,2 @@
|
|||
source_up
|
||||
use_nix
|
126
users/wpcarro/scratch/deepmind/part_two/balanced-binary-tree.py
Normal file
126
users/wpcarro/scratch/deepmind/part_two/balanced-binary-tree.py
Normal 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)
|
110
users/wpcarro/scratch/deepmind/part_two/bst-checker.py
Normal file
110
users/wpcarro/scratch/deepmind/part_two/bst-checker.py
Normal 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)
|
|
@ -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)
|
102
users/wpcarro/scratch/deepmind/part_two/coin.ts
Normal file
102
users/wpcarro/scratch/deepmind/part_two/coin.ts
Normal 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}`);
|
||||
}
|
||||
}
|
57
users/wpcarro/scratch/deepmind/part_two/delete-node.py
Normal file
57
users/wpcarro/scratch/deepmind/part_two/delete-node.py
Normal 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)
|
|
@ -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)
|
|
@ -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}`);
|
||||
}
|
||||
}
|
|
@ -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}`);
|
||||
}
|
||||
}
|
232
users/wpcarro/scratch/deepmind/part_two/graph-coloring.ts
Normal file
232
users/wpcarro/scratch/deepmind/part_two/graph-coloring.ts
Normal 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`);
|
||||
}
|
||||
}
|
|
@ -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=".")
|
|
@ -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}`);
|
||||
}
|
||||
}
|
|
@ -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`);
|
||||
}
|
||||
}
|
115
users/wpcarro/scratch/deepmind/part_two/merging-ranges.py
Normal file
115
users/wpcarro/scratch/deepmind/part_two/merging-ranges.py
Normal 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)
|
183
users/wpcarro/scratch/deepmind/part_two/mesh-message.py
Normal file
183
users/wpcarro/scratch/deepmind/part_two/mesh-message.py
Normal 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)
|
|
@ -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))
|
72
users/wpcarro/scratch/deepmind/part_two/nth-fibonacci.py
Normal file
72
users/wpcarro/scratch/deepmind/part_two/nth-fibonacci.py
Normal 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)
|
79
users/wpcarro/scratch/deepmind/part_two/package-lock.json
generated
Normal file
79
users/wpcarro/scratch/deepmind/part_two/package-lock.json
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
16
users/wpcarro/scratch/deepmind/part_two/package.json
Normal file
16
users/wpcarro/scratch/deepmind/part_two/package.json
Normal 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"
|
||||
}
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
|
@ -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`);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
74
users/wpcarro/scratch/deepmind/part_two/reverse-words.py
Normal file
74
users/wpcarro/scratch/deepmind/part_two/reverse-words.py
Normal 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)
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue