def test_get_next__successor_complex(): A = BinaryNode(1) C = BinaryNode(3) B = BinaryNode(2, left=A, right=C) D = BinaryNode(4, left=B, right=None) assert get_next(A).value == 2 assert get_next(B).value == 3 assert get_next(C).value == 4 assert get_next(D) is None
def test_find__something_in_the_middle(): root = BinaryNode(5) root.left = BinaryNode(3) root.right = BinaryNode(7) root.left.left = BinaryNode(2) root.left.right = BinaryNode(4) root.right.left = BinaryNode(6) root.right.right = BinaryNode(8) assert find(root.left.left, root.left.right) == root.left
def to_bst(array): """ One way to approach this problem is to pick the middle value, and then recursively build the BST from the root down by picking middle values for the left and the right of the root. This can be a bit tedious by having to perform index arithmetic, and I hate index arithmetic. One thing that is nice about the question is that we are given a total ordering, so this means that for any element in the array, we have a guarantee that the previous element is less than, and the next element is greater than, strictly. This allows us to build the BST in layers, like a cake. In the first pass, every other element in the array is taken and these become the leaf nodes of our BST. They are added to an "orphans" queue. In the next pass, we take every other element again, only this time, it checks the orphans queue to assign left and right nodes. I think the runtime is something like O(log^2 n) because we make at most log n passes over the number of elements, but each time, we are only working with half the number of elements from the previous step. """ @dataclass class MarkedElement: value: object marked: bool = False elements = [MarkedElement(value=v) for v in array] orphans = deque([]) def unmarked(elements): return list(filter(lambda e: not e.marked, elements)) parents = deque([]) while len(unmarked(elements)) > 0: for index, element in enumerate(unmarked(elements)): if index % 2 == 0: element.marked = True parent = BinaryNode(element.value, left=safe_popleft(orphans), right=safe_popleft(orphans)) parents.append(parent) parents.reverse() orphans.extendleft(parents) parents.clear() # the final orphan is the root of the tree root = safe_popleft(orphans) return root
def test_check__none_cannot_have_subtrees(): assert not check(None, BinaryNode(1))
def test_check__none_is_subtree(): assert check(BinaryNode(1), None)
def test_check__complex_subtree(): tree = BinaryNode(5) tree.left = BinaryNode(3) tree.right = BinaryNode(7) tree.left.left = BinaryNode(2) tree.left.right = BinaryNode(4) tree.right.left = BinaryNode(6) tree.right.right = BinaryNode(8) assert check(tree, BinaryNode(7, left=BinaryNode(6), right=BinaryNode(8)))
def test_check__against_self(): tree = BinaryNode(2, left=BinaryNode(1), right=BinaryNode(3)) assert check(tree, tree)
def test_check__single_node_subtree(): tree = BinaryNode(2, left=BinaryNode(1), right=BinaryNode(3)) assert check(tree, BinaryNode(1)) assert check(tree, BinaryNode(3))
def test_get_next__no_successor_self_is_at_root(): assert get_next(BinaryNode(1)) is None
def test_get_next__parent_is_successor(): node = BinaryNode(2, left=BinaryNode(1)) assert get_next(node.left).value == 2
def test_get_next__simple_successor(): node = BinaryNode(1, right=BinaryNode(2)) assert get_next(node).value == 2
def test_is_bst__not_at_all(): root = BinaryNode(7, left=BinaryNode(9), right=BinaryNode(1)) assert not is_bst(root)
def test_is_bst__simple(): root = BinaryNode(2, left=BinaryNode(1), right=None) assert is_bst(root)
def test_is_bst__one(): assert is_bst(BinaryNode(1))
def test_find__only_the_root(): root = BinaryNode(1) assert find(root, root) == root
def test_find__it_is_the_root(): left = BinaryNode(1) right = BinaryNode(9) root = BinaryNode(5, left=left, right=right) assert find(left, right) == root