i = next( j for j, y in enumerate(path) if x > y.val and y.right is None) # highest insertion point while len(path) > i + 1: yield path.pop().val c = path[-1] # equivalent to path[i] c.right = Node(None, x, None) path.append(c.right) while len(path) > 0: # pop everything yield path.pop().val if __name__ == '__main__': from lib import fails_as, iter_equals from trees.construction import random_bst from trees.traversal import pre_order, post_order std_test = { (40, 30, 35, 80, 100): (35, 30, 100, 80, 40), (40, 30, 32, 35, 80, 90, 100, 120): (35, 32, 30, 120, 100, 90, 80, 40), (40, 30, 10, 35, 37, 80, 70, 60, 75, 100, 90): (10, 37, 35, 30, 60, 75, 70, 90, 100, 80, 40) } for k, v in std_test.items(): assert iter_equals(convert(k), v) fail_test = {(7, 9, 6, 1, 4, 2, 3, 40), (40, 30, 35, 20, 80, 100)} for x in fail_test: assert fails_as(AssertionError, lambda y: tuple(convert(y)), x) for size in [x for x in range(1, 100) for _ in range(x)]: t = random_bst(size) assert iter_equals(convert(pre_order(t)), post_order(t))
s.append((x.left, False)) def fast_post_order(root): s = [(root, 0)] while len(s) > 0: x, v = s[ -1] # v = 0: left child unvisited; 1: left child visited, but right child not; 2: right child visited if x is None: s.pop() continue if v == 0: s[-1] = x, 1 s.append((x.left, 0)) elif v == 1: s[-1] = x, 2 s.append((x.right, 0)) else: yield x.val s.pop() if __name__ == '__main__': from lib import iter_equals from trees.construction import random_bst for size in range(1, 100): t = random_bst(size) assert iter_equals(pre_order(t), fast_pre_order(t)) assert iter_equals(in_order(t), fast_in_order(t)) assert iter_equals(post_order(t), fast_post_order(t))
from collections import deque def product(*iters, reverse=False): """ Recursion-free implementation of itertools.product with manually managed stack/queue. Time complexity is O(\prod n_i), where n_i is the size of each iterator. Space complexity is O(\prod n_i). :param iters: *iterable[T]. As in itertools.product, each iterable must be non-empty, otherwise nothing is generated. :param reverse: bool. product(..., reverse=True) is equivalent to product(..., reverse=False) with each iterable in reverse order. A stack is used instead of a queue for this purpose. :return: generator[tuple[*T]]. """ n = len(iters) q = deque() q.append(([], 0)) # deque[tuple[list[T],int]] while q: xs, i = q.pop() if reverse else q.popleft() if i == n: yield tuple(xs) else: for y in iters[i]: q.append((xs + [y], i + 1)) if __name__ == '__main__': from itertools import product as control from lib import iter_equals xs = [1, 2], [3, 4, 5], [6, 7, 8, 9] assert iter_equals(control(*xs), product(*xs)) assert iter_equals(list(control(*xs))[::-1], product(*xs, reverse=True))
x, c = pseudo_root, 0 while x.right is not None: x = x.right c += 1 vine_to_tree(pseudo_root, c) return pseudo_root.right, c def max_height_diff(root): def step(x): if x is None: return 0, 0 l_depth, l_diff = step(x.left) # recursive call r_depth, r_diff = step(x.right) # recursive call depth = max(l_depth, r_depth) + 1 diff = max(l_diff, r_diff, abs(l_depth - r_depth)) return depth, diff return step(root)[1] if __name__ == '__main__': from lib import iter_equals from trees.construction import random_bst from trees.traversal import in_order for size in [x for x in range(1, 100) for _ in range(x)]: t = random_bst(size) d = max_height_diff(t) control = list(in_order(t)) r, c = day_stout_warren(t) assert c == size assert iter_equals(in_order(r), control) assert max_height_diff(r) <= d
binary tree. Assumes the input is a valid pre-order traversal sequence, and elements in the BST to be unique. :param seq: seq[T], where T is comparable :return: Node """ ite = iter(seq) x = next(ite, None) assert x is not None # empty iterator root = Node(None, x, None) s = [root] for x in ite: # iterate through seq[1:] new = Node(None, x, None) if x < s[-1].val: s[-1].left = new else: # x > s[-1].val while len(s) > 0 and x > s[-1].val: last = s.pop() last.right = new # select the highest insertion point s.append(new) return root if __name__ == '__main__': from lib import iter_equals from trees.construction import random_bst from trees.traversal import fast_pre_order, in_order, fast_post_order for size in [x for x in range(1, 100) for _ in range(x)]: t = random_bst(size) r = rebuild(fast_pre_order(t)) assert iter_equals(in_order(r), in_order(t)) assert iter_equals(fast_post_order(r), fast_post_order(t))
c += 1 vine_to_tree(pseudo_root, c) return pseudo_root.right, c def max_height_diff(root): def step(x): if x is None: return 0, 0 l_depth, l_diff = step(x.left) # recursive call r_depth, r_diff = step(x.right) # recursive call depth = max(l_depth, r_depth) + 1 diff = max(l_diff, r_diff, abs(l_depth - r_depth)) return depth, diff return step(root)[1] if __name__ == '__main__': from lib import iter_equals from trees.construction import random_bst from trees.traversal import in_order for size in [x for x in range(1, 100) for _ in range(x)]: t = random_bst(size) d = max_height_diff(t) control = list(in_order(t)) r, c = day_stout_warren(t) assert c == size assert iter_equals(in_order(r), control) assert max_height_diff(r) <= d
s.append((x.right, False)) else: s[-1] = x, True s.append((x.left, False)) def fast_post_order(root): s = [(root, 0)] while len(s) > 0: x, v = s[-1] # v = 0: left child unvisited; 1: left child visited, but right child not; 2: right child visited if x is None: s.pop() continue if v == 0: s[-1] = x, 1 s.append((x.left, 0)) elif v == 1: s[-1] = x, 2 s.append((x.right, 0)) else: yield x.val s.pop() if __name__ == '__main__': from lib import iter_equals from trees.construction import random_bst for size in range(1, 100): t = random_bst(size) assert iter_equals(pre_order(t), fast_pre_order(t)) assert iter_equals(in_order(t), fast_in_order(t)) assert iter_equals(post_order(t), fast_post_order(t))
if x < c.val: # x is either the left child of cursor, or is an invalid input for y in path[:-1]: # insert x from the root. it should arrive at cursor assert x > y.val or y.left is not None c.left = Node(None, x, None) path.append(c.left) else: # x is either the right child of cursor, or a right child of an ancestor of cursor i = next(j for j, y in enumerate(path) if x > y.val and y.right is None) # highest insertion point while len(path) > i + 1: yield path.pop().val c = path[-1] # equivalent to path[i] c.right = Node(None, x, None) path.append(c.right) while len(path) > 0: # pop everything yield path.pop().val if __name__ == '__main__': from lib import fails_as, iter_equals from trees.construction import random_bst from trees.traversal import pre_order, post_order std_test = {(40, 30, 35, 80, 100): (35, 30, 100, 80, 40), (40, 30, 32, 35, 80, 90, 100, 120): (35, 32, 30, 120, 100, 90, 80, 40), (40, 30, 10, 35, 37, 80, 70, 60, 75, 100, 90): (10, 37, 35, 30, 60, 75, 70, 90, 100, 80, 40)} for k, v in std_test.items(): assert iter_equals(convert(k), v) fail_test = {(7, 9, 6, 1, 4, 2, 3, 40), (40, 30, 35, 20, 80, 100)} for x in fail_test: assert fails_as(AssertionError, lambda y: tuple(convert(y)), x) for size in [x for x in range(1, 100) for _ in range(x)]: t = random_bst(size) assert iter_equals(convert(pre_order(t)), post_order(t))
# since __bool__ is not defined in Link, bool(link) is always True unless link is None yield y.key def remove_duplicates(head): # in-place, O(n^2) time left = head while left is not None: probe = left # inspect probe.right while probe is not None: if probe.right is not None and probe.right.key == left.key: probe.right = probe.right.right else: # if probe.right is removed, then do not move forward. consider [0, -6, 7, 4, 5, -6, -6] probe = probe.right left = left.right if __name__ == '__main__': from lib import iter_equals from random import randint def control(arr): appeared = set() for x in arr: if x not in appeared: yield x appeared.add(x) for size in [x for x in range(50) for _ in range(x)]: a = [randint(-size, size) for _ in range(size)] ll = LinkedList(reversed(a)) ll.reverse() assert iter_equals(ll, a) remove_duplicates(ll.head) assert iter_equals(ll, control(a))