From ca2f69472cde77737124efde5ae3716b763c8734 Mon Sep 17 00:00:00 2001 From: Kevin Nowaczyk Date: Mon, 20 Nov 2023 15:37:20 -0500 Subject: [PATCH 1/3] Test print --- src/rbtree.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/rbtree.py b/src/rbtree.py index f910cd4..3bb0a2e 100644 --- a/src/rbtree.py +++ b/src/rbtree.py @@ -81,6 +81,15 @@ def __getitem__(self: T, key: int) -> int: def __setitem__(self: T, key: int, value: int) -> None: self.search(key).value = value + def __str__(self: T) -> str: + node = self.root + output = "" + s_color = "RED" if node.is_red() else "BLACK" + output += str(node.key) + "(" + s_color + ")\n" + output += self.__print_helper(node.left, " ", False) + output += self.__print_helper(node.right, " ", True) + return output + # Setters and Getters # def get_root(self: T) -> Node: return self.root @@ -296,19 +305,21 @@ def fix_insert(self: T, node: Node) -> None: # Printing the tree def __print_helper(self: T, node: Node, indent: str, last: bool) -> None: + output = "" if not node.is_null(): sys.stdout.write(indent) if last: - sys.stdout.write("R---- ") + output += "R---- " indent += " " else: - sys.stdout.write("L---- ") + output += "L---- " indent += "| " s_color = "RED" if node.is_red() else "BLACK" - print(str(node.get_key()) + "(" + s_color + ")") - self.__print_helper(node.left, indent, False) - self.__print_helper(node.right, indent, True) + output += str(node.get_key()) + "(" + s_color + ")" + '\n' + output += self.__print_helper(node.left, indent, False) + output += self.__print_helper(node.right, indent, True) + return output def search(self: T, key: int) -> Node: return self.search_tree_helper(self.root, key) @@ -423,4 +434,4 @@ def delete(self: T, key: int) -> None: self.delete_node_helper(self.root, key) def print_tree(self: T) -> None: - self.__print_helper(self.root, "", True) + print str(self) From e0ffc332ff921a2f0dc5e183f252db13387b189f Mon Sep 17 00:00:00 2001 From: Kevin Nowaczyk Date: Tue, 21 Nov 2023 10:58:31 -0500 Subject: [PATCH 2/3] Update test_rbtree.py (#22) --- src/rbtree.py | 13 +++++------ tests/test_node.py | 11 +++++++++ tests/test_rbtree.py | 54 ++++++++++++++++++++++++++++---------------- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/src/rbtree.py b/src/rbtree.py index 3bb0a2e..a527e51 100644 --- a/src/rbtree.py +++ b/src/rbtree.py @@ -1,7 +1,6 @@ # Implementing Red-Black Tree in Python # Adapted from https://www.programiz.com/dsa/red-black-tree -import sys from typing import Type, TypeVar, Iterator @@ -85,11 +84,11 @@ def __str__(self: T) -> str: node = self.root output = "" s_color = "RED" if node.is_red() else "BLACK" - output += str(node.key) + "(" + s_color + ")\n" + output += str(node.get_key()) + "(" + s_color + ")\n" output += self.__print_helper(node.left, " ", False) output += self.__print_helper(node.right, " ", True) return output - + # Setters and Getters # def get_root(self: T) -> Node: return self.root @@ -304,15 +303,15 @@ def fix_insert(self: T, node: Node) -> None: self.root.set_color("black") # Printing the tree - def __print_helper(self: T, node: Node, indent: str, last: bool) -> None: + def __print_helper(self: T, node: Node, indent: str, last: bool) -> str: output = "" if not node.is_null(): - sys.stdout.write(indent) + output += indent if last: output += "R---- " indent += " " else: - output += "L---- " + output += "L---- " indent += "| " s_color = "RED" if node.is_red() else "BLACK" @@ -434,4 +433,4 @@ def delete(self: T, key: int) -> None: self.delete_node_helper(self.root, key) def print_tree(self: T) -> None: - print str(self) + print(str(self)) diff --git a/tests/test_node.py b/tests/test_node.py index 0ad9ffc..09f97ef 100644 --- a/tests/test_node.py +++ b/tests/test_node.py @@ -66,3 +66,14 @@ def test_null_node() -> None: null = Node.null() assert null.is_null() assert null.is_black() + + +def test_repr() -> None: + """ + Test: + Key only + Key/Value + Null + """ + node = Node(2) + assert repr(node) == "Key: 2 Value: None" diff --git a/tests/test_rbtree.py b/tests/test_rbtree.py index 7bdb21f..c7c3e9c 100644 --- a/tests/test_rbtree.py +++ b/tests/test_rbtree.py @@ -257,25 +257,6 @@ def test_iterator_exception() -> None: bst.set_iteration_style("spam") -def test_print() -> None: - bst = RedBlackTree() - bst.insert(73) - print(bst.get_root()) - bst.insert(48) - bst.insert(100) - bst.insert(42) - bst.insert(55) - bst.insert(40) - bst.insert(58) - bst.insert(42) - bst.insert(55) - bst.insert(40) - bst.insert(58) - bst.insert(42) - - bst.print_tree() - - def test_elaborate_delete() -> None: bst = RedBlackTree() bst.insert(55) @@ -355,3 +336,38 @@ def test_duplicates() -> None: bst.delete(42) bst.delete(42) check_valid(bst) + + +print_data = [ + [ + [1, 2, 3], + ("" + + "2(BLACK)\n" + + " L---- 1(RED)\n" + + " R---- 3(RED)\n") + ], + [ + [73, 48, 100, 42, 55, 40, 58, 42, 55, 40, 58, 42], + ("" + + "48(BLACK)\n" + + " L---- 42(BLACK)\n" + + " | L---- 40(BLACK)\n" + + " | | R---- 40(RED)\n" + + " | R---- 42(BLACK)\n" + + " | R---- 42(RED)\n" + + " R---- 73(BLACK)\n" + + " L---- 55(RED)\n" + + " | L---- 55(BLACK)\n" + + " | R---- 58(BLACK)\n" + + " | R---- 58(RED)\n" + + " R---- 100(BLACK)\n") + ] +] + + +@pytest.mark.parametrize("input, expected", print_data) +def test_print(input: list, expected: str) -> None: + bst = RedBlackTree() + for key in input: + bst.insert(key) + assert str(bst) == expected From b74719d45e04999bf607af86fa9da528648cb2ce Mon Sep 17 00:00:00 2001 From: Kevin Nowaczyk Date: Thu, 23 Nov 2023 07:58:37 -0500 Subject: [PATCH 3/3] Patch 2 (#26) * Update email * Update status badge * Update requirements_dev.txt * Update test_node.py * Update python-package.yml * Oops * Update README.md * Update test_rbtree.py * Create iterators (#15) * Update README.md * Rename item to key and make private (#17) * Update rbtree.py * Update Tests * Update rbtree.py * Update rbtree.py * Update rbtree.py * Added len() * Added tests --------- Co-authored-by: Emily Dolson --- .github/workflows/python-package.yml | 3 +- README.md | 25 ++- pyproject.toml | 2 +- requirements_dev.txt | 2 +- src/rbtree.py | 226 ++++++++++++++++----------- tests/test_node.py | 23 +++ tests/test_rbtree.py | 121 ++++++++++---- 7 files changed, 271 insertions(+), 131 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index a2f612c..49ca756 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -41,7 +41,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | pytest --cov=src - coveralls --service=github + - name: Upload Coverage to Codecov + uses: codecov/codecov-action@v1 flake8: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 4c5f844..4df9988 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Python red-black trees -[![Python application](https://github.com/emilydolson/python-red-black-trees/actions/workflows/python-app.yml/badge.svg)](https://github.com/emilydolson/python-red-black-trees/actions/workflows/python-app.yml) [![codecov](https://codecov.io/gh/emilydolson/python-red-black-trees/branch/main/graph/badge.svg?token=0LAOX0AEZY)](https://codecov.io/gh/emilydolson/python-red-black-trees) +[![Python package](https://github.com/emilydolson/python-red-black-trees/actions/workflows/python-package.yml/badge.svg)](https://github.com/emilydolson/python-red-black-trees/actions/workflows/python-package.yml) [![codecov](https://codecov.io/gh/emilydolson/python-red-black-trees/branch/main/graph/badge.svg?token=0LAOX0AEZY)](https://codecov.io/gh/emilydolson/python-red-black-trees) A Python implementation of red-black trees. This code was originally copied from [programiz.com](https://www.programiz.com/dsa/red-black-tree), but I have made a few tweaks to improve the user interface. I have also fixed a hard-to-catch but serious bug in the original implementation (which has since been propogated to a number of tutorials on the internet), and added a rigorous testing suite to ensure there are no further bugs. @@ -77,15 +77,28 @@ bst.successor(bst.search(6)) # Gets the successor a node containing 6 ``` -#### Printing methods +#### Printing -To know more about the contents of the tree, you can use various printing methods: +To know more about the contents of the tree, you can print it to stdout: ``` bst.print_tree() # prints an ASCII representation of the whole tree -bst.preorder() # prints a preorder traversal -bst.inorder() # prints an inorder traveral -bst.postorder() # prints a postorder traversal +``` + +#### Traversals + +To contents of the tree can be collected into an array in any of three ways. +The tree itself can be used anywhere a collection is used. + +``` +bst.preorder() # creates a preorder traversal list +bst.inorder() # creates an inorder traveral list +bst.postorder() # creates a postorder traversal list + +key_string = "" +bst.set_iteration_style("pre") +for node in bst: + key_string += str(node.get_key()) + " " ``` ### Dictionary interface diff --git a/pyproject.toml b/pyproject.toml index fdfb142..edcbf04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" name = "python-red-black-trees" version = "0.0.1" authors = [ - { name="Emily Dolson", email="me@me.com" }, + { name="Emily Dolson", email="emilyldolson@gmail.com" }, ] description = "Red-black tree implementation in Python." readme = "README.md" diff --git a/requirements_dev.txt b/requirements_dev.txt index 8b3978e..44d45a0 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,5 +1,5 @@ pytest pytest-cov -coveralls +pytest-codecov pep8-naming flake8-annotations diff --git a/src/rbtree.py b/src/rbtree.py index 99e2173..311acaf 100644 --- a/src/rbtree.py +++ b/src/rbtree.py @@ -2,7 +2,7 @@ # Adapted from https://www.programiz.com/dsa/red-black-tree import sys -from typing import TypeVar +from typing import Type, TypeVar, Iterator T = TypeVar('T', bound='Node') @@ -10,86 +10,148 @@ # Node creation class Node(): - next_id = 0 - def __init__(self: T, item: int) -> None: - self.id = Node.next_id - Node.next_id += 1 - self.item = item + def __init__(self: T, key: int) -> None: + self._key = key self.parent = None self.left = None self.right = None - self.color = 1 + self._color = 1 self.value = None - def __eq__(self: T, other: T) -> bool: - return self.id == other.id - def __repr__(self: T) -> str: - return "ID: " + str(self.id) + " Value: " + str(self.item) + return "Key: " + str(self._key) + " Value: " + str(self.value) def get_color(self: T) -> str: - return "black" if self.color == 0 else "red" + return "black" if self._color == 0 else "red" def set_color(self: T, color: str) -> None: if color == "black": - self.color = 0 + self._color = 0 elif color == "red": - self.color = 1 + self._color = 1 else: raise Exception("Unknown color") + def get_key(self: T) -> int: + return self._key + def is_red(self: T) -> bool: - return self.color == 1 + return self._color == 1 def is_black(self: T) -> bool: - return self.color == 0 + return self._color == 0 def is_null(self: T) -> bool: - return self.id == -1 + return self._key is None def depth(self: T) -> int: return 0 if self.parent is None else self.parent.depth() + 1 + @classmethod + def null(cls: Type[T]) -> T: + node = cls(0) + node._key = None + node.set_color("black") + return node + T = TypeVar('T', bound='RedBlackTree') class RedBlackTree(): def __init__(self: T) -> None: - self.TNULL = Node(0) - self.TNULL.id = -1 - self.TNULL.set_color("black") + self.TNULL = Node.null() self.root = self.TNULL self.size = 0 + self._iter_format = 0 - # Preorder - def pre_order_helper(self: T, node: Node) -> None: - if not node.is_null(): - sys.stdout.write(str(node.item) + " ") - self.pre_order_helper(node.left) - self.pre_order_helper(node.right) + # Dunder Methods # + def __iter__(self: T) -> Iterator: + if self._iter_format == 0: + return iter(self.preorder()) + if self._iter_format == 1: + return iter(self.inorder()) + if self._iter_format == 2: + return iter(self.postorder()) - # Inorder - def in_order_helper(self: T, node: Node) -> None: - if not node.is_null(): - self.in_order_helper(node.left) - sys.stdout.write(str(node.item) + " ") - self.in_order_helper(node.right) + def __getitem__(self: T, key: int) -> int: + return self.search(key).value + + def __setitem__(self: T, key: int, value: int) -> None: + self.search(key).value = value + + def __len__(self: T) -> int: + return self.size + + # Setters and Getters # + def get_root(self: T) -> Node: + return self.root + + def set_iteration_style(self: T, style: str) -> None: + if style == "pre": + self._iter_format = 0 + elif style == "in": + self._iter_format = 1 + elif style == "post": + self._iter_format = 2 + else: + raise Exception("Unknown style.") - # Postorder - def post_order_helper(self: T, node: Node) -> None: + # Iterators # + def preorder(self: T) -> list: + return self.pre_order_helper(self.root) + + def inorder(self: T) -> list: + return self.in_order_helper(self.root) + + def postorder(self: T) -> list: + return self.post_order_helper(self.root) + + def pre_order_helper(self: T, node: Node) -> list: + """ + Perform a preorder tree traversal starting at the + given node. + """ + output = [] + if not node.is_null(): + left = self.pre_order_helper(node.left) + right = self.pre_order_helper(node.right) + output.extend([node]) + output.extend(left) + output.extend(right) + return output + + def in_order_helper(self: T, node: Node) -> list: + """ + Perform a inorder tree traversal starting at the + given node. + """ + output = [] + if not node.is_null(): + left = self.in_order_helper(node.left) + right = self.in_order_helper(node.right) + output.extend(left) + output.extend([node]) + output.extend(right) + return output + + def post_order_helper(self: T, node: Node) -> list: + output = [] if not node.is_null(): - self.post_order_helper(node.left) - self.post_order_helper(node.right) - sys.stdout.write(str(node.item) + " ") + left = self.post_order_helper(node.left) + right = self.post_order_helper(node.right) + output.extend(left) + output.extend(right) + output.extend([node]) + return output # Search the tree def search_tree_helper(self: T, node: Node, key: int) -> Node: - if node.is_null() or key == node.item: + if node.is_null() or key == node.get_key(): return node - if key < node.item: + if key < node.get_key(): return self.search_tree_helper(node.left, key) return self.search_tree_helper(node.right, key) @@ -157,10 +219,10 @@ def __rb_transplant(self: T, u: Node, v: Node) -> None: def delete_node_helper(self: T, node: Node, key: int) -> None: z = self.TNULL while not node.is_null(): - if node.item == key: + if node.get_key() == key: z = node - if node.item <= key: + if node.get_key() <= key: node = node.right else: node = node.left @@ -200,38 +262,38 @@ def delete_node_helper(self: T, node: Node, key: int) -> None: self.size -= 1 # Balance the tree after insertion - def fix_insert(self: T, k: Node) -> None: - while k.parent.is_red(): - if k.parent == k.parent.parent.right: - u = k.parent.parent.left + def fix_insert(self: T, node: Node) -> None: + while node.parent.is_red(): + if node.parent == node.parent.parent.right: + u = node.parent.parent.left if u.is_red(): u.set_color("black") - k.parent.set_color("black") - k.parent.parent.set_color("red") - k = k.parent.parent + node.parent.set_color("black") + node.parent.parent.set_color("red") + node = node.parent.parent else: - if k == k.parent.left: - k = k.parent - self.right_rotate(k) - k.parent.set_color("black") - k.parent.parent.set_color("red") - self.left_rotate(k.parent.parent) + if node == node.parent.left: + node = node.parent + self.right_rotate(node) + node.parent.set_color("black") + node.parent.parent.set_color("red") + self.left_rotate(node.parent.parent) else: - u = k.parent.parent.right + u = node.parent.parent.right if u.is_red(): u.set_color("black") - k.parent.set_color("black") - k.parent.parent.set_color("red") - k = k.parent.parent + node.parent.set_color("black") + node.parent.parent.set_color("red") + node = node.parent.parent else: - if k == k.parent.right: - k = k.parent - self.left_rotate(k) - k.parent.set_color("black") - k.parent.parent.set_color("red") - self.right_rotate(k.parent.parent) - if k == self.root: + if node == node.parent.right: + node = node.parent + self.left_rotate(node) + node.parent.set_color("black") + node.parent.parent.set_color("red") + self.right_rotate(node.parent.parent) + if node == self.root: break self.root.set_color("black") @@ -247,21 +309,12 @@ def __print_helper(self: T, node: Node, indent: str, last: bool) -> None: indent += "| " s_color = "RED" if node.is_red() else "BLACK" - print(str(node.item) + "(" + s_color + ")") + print(str(node.get_key()) + "(" + s_color + ")") self.__print_helper(node.left, indent, False) self.__print_helper(node.right, indent, True) - def preorder(self: T) -> None: - self.pre_order_helper(self.root) - - def inorder(self: T) -> None: - self.in_order_helper(self.root) - - def postorder(self: T) -> None: - self.post_order_helper(self.root) - - def search(self: T, k: int) -> Node: - return self.search_tree_helper(self.root, k) + def search(self: T, key: int) -> Node: + return self.search_tree_helper(self.root, key) def minimum(self: T, node: Node = None) -> Node: if node is None: @@ -336,8 +389,6 @@ def right_rotate(self: T, x: Node) -> None: def insert(self: T, key: int) -> None: node = Node(key) - node.parent = None - node.item = key node.left = self.TNULL node.right = self.TNULL node.set_color("red") @@ -347,7 +398,7 @@ def insert(self: T, key: int) -> None: while not x.is_null(): y = x - if node.item < x.item: + if node.get_key() < x.get_key(): x = x.left else: x = x.right @@ -355,7 +406,7 @@ def insert(self: T, key: int) -> None: node.parent = y if y is None: self.root = node - elif node.item < y.item: + elif node.get_key() < y.get_key(): y.left = node else: y.right = node @@ -371,17 +422,8 @@ def insert(self: T, key: int) -> None: self.fix_insert(node) - def get_root(self: T) -> Node: - return self.root - - def delete(self: T, item: int) -> None: - self.delete_node_helper(self.root, item) + def delete(self: T, key: int) -> None: + self.delete_node_helper(self.root, key) def print_tree(self: T) -> None: self.__print_helper(self.root, "", True) - - def __getitem__(self: T, key: int) -> int: - return self.search(key).value - - def __setitem__(self: T, key: int, value: int) -> None: - self.search(key).value = value diff --git a/tests/test_node.py b/tests/test_node.py index 9f37aae..0ad9ffc 100644 --- a/tests/test_node.py +++ b/tests/test_node.py @@ -1,4 +1,6 @@ +import pytest from rbtree import Node +from typing import Any def test_constructor() -> None: @@ -43,3 +45,24 @@ def test_depth() -> None: second_node.parent = node node.left = second_node assert second_node.depth() == 1 + + +def test_color_exception() -> None: + node = Node(0) + with pytest.raises(Exception): + node.set_color("spam") + + +get_key_data = [-1, 0, 42] + + +@pytest.mark.parametrize("key", get_key_data) +def test_get_key(key: Any) -> None: + node = Node(key) + assert node.get_key() == key + + +def test_null_node() -> None: + null = Node.null() + assert null.is_null() + assert null.is_black() diff --git a/tests/test_rbtree.py b/tests/test_rbtree.py index 6fb544c..d720878 100644 --- a/tests/test_rbtree.py +++ b/tests/test_rbtree.py @@ -1,3 +1,4 @@ +import pytest from rbtree import RedBlackTree, Node @@ -11,9 +12,9 @@ def check_node_valid(bst: RedBlackTree, node: Node) -> None: assert node.right.is_black() if not node.left.is_null() and node.left is not None: - assert node.item >= node.left.item + assert node.get_key() >= node.left.get_key() if not node.right.is_null() and node.right is not None: - assert node.item <= node.right.item + assert node.get_key() <= node.right.get_key() def check_valid_recur(bst: RedBlackTree, node: Node) -> int: @@ -51,13 +52,13 @@ def check_valid(bst: RedBlackTree) -> None: def test_insert() -> None: bst = RedBlackTree() bst.insert(55) - assert bst.search(55).item == 55 + assert bst.search(55).get_key() == 55 bst.insert(40) - assert bst.search(40).item == 40 + assert bst.search(40).get_key() == 40 bst.insert(58) - assert bst.search(58).item == 58 + assert bst.search(58).get_key() == 58 bst.insert(42) - assert bst.search(42).item == 42 + assert bst.search(42).get_key() == 42 bst.insert(42) bst.insert(42) @@ -79,7 +80,7 @@ def test_insert() -> None: bst.insert(109) bst.insert(102) - assert bst.size == 23 + assert len(bst) == 23 check_valid(bst) @@ -88,13 +89,13 @@ def test_search() -> None: bst = RedBlackTree() assert bst.search(60).is_null() bst.insert(30) - assert bst.search(30).item == 30 + assert bst.search(30).get_key() == 30 def test_delete() -> None: bst = RedBlackTree() bst.insert(78) - assert bst.search(78).item == 78 + assert bst.search(78).get_key() == 78 bst.delete(78) assert bst.search(78).is_null() @@ -111,24 +112,24 @@ def test_delete() -> None: bst.insert(58) bst.insert(42) - assert bst.size == 12 + assert len(bst) == 12 bst.delete(48) - assert bst.size == 11 + assert len(bst) == 11 bst.delete(42) - assert bst.size == 10 + assert len(bst) == 10 bst.delete(42) - assert bst.size == 9 - assert bst.search(42).item == 42 + assert len(bst) == 9 + assert bst.search(42).get_key() == 42 bst.delete(42) assert bst.search(42).is_null() - assert bst.size == 8 + assert len(bst) == 8 bst.delete(100) - assert bst.size == 7 + assert len(bst) == 7 bst.delete(100) - assert bst.size == 7 + assert len(bst) == 7 check_valid(bst) @@ -167,7 +168,7 @@ def test_get_root() -> None: bst = RedBlackTree() assert bst.get_root().is_null() bst.insert(3) - assert bst.get_root().item == 3 + assert bst.get_root().get_key() == 3 def test_accessors() -> None: @@ -180,17 +181,80 @@ def test_accessors() -> None: bst.insert(58) bst.insert(42) - assert bst.maximum().item == 58 - assert bst.minimum().item == 40 - assert bst.successor(bst.search(42)).item == 55 - assert bst.successor(bst.search(40)).item == 42 - assert bst.successor(bst.search(55)).item == 58 - assert bst.predecessor(bst.search(42)).item == 40 - assert bst.predecessor(bst.search(55)).item == 42 - assert bst.predecessor(bst.search(58)).item == 55 + assert bst.maximum().get_key() == 58 + assert bst.minimum().get_key() == 40 + assert bst.successor(bst.search(42)).get_key() == 55 + assert bst.successor(bst.search(40)).get_key() == 42 + assert bst.successor(bst.search(55)).get_key() == 58 + assert bst.predecessor(bst.search(42)).get_key() == 40 + assert bst.predecessor(bst.search(55)).get_key() == 42 + assert bst.predecessor(bst.search(58)).get_key() == 55 bst.insert(57) - assert bst.predecessor(bst.search(57)).item == 55 + assert bst.predecessor(bst.search(57)).get_key() == 55 + + +def test_preorder() -> None: + bst = RedBlackTree() + bst.insert(1) + bst.insert(2) + bst.insert(3) + + nodes = bst.preorder() + keys = [] + for node in nodes: + keys.append(str(node.get_key())) + assert " ".join(keys) == "2 1 3" + + keys = [] + bst.set_iteration_style("pre") + for node in bst: + keys.append(str(node.get_key())) + assert " ".join(keys) == "2 1 3" + + +def test_inorder() -> None: + bst = RedBlackTree() + bst.insert(1) + bst.insert(2) + bst.insert(3) + + nodes = bst.inorder() + keys = [] + for node in nodes: + keys.append(str(node.get_key())) + assert " ".join(keys) == "1 2 3" + + keys = [] + bst.set_iteration_style("in") + for node in bst: + keys.append(str(node.get_key())) + assert " ".join(keys) == "1 2 3" + + +def test_postorder() -> None: + bst = RedBlackTree() + bst.insert(1) + bst.insert(2) + bst.insert(3) + + nodes = bst.postorder() + keys = [] + for node in nodes: + keys.append(str(node.get_key())) + assert " ".join(keys) == "1 3 2" + + keys = [] + bst.set_iteration_style("post") + for node in bst: + keys.append(str(node.get_key())) + assert " ".join(keys) == "1 3 2" + + +def test_iterator_exception() -> None: + bst = RedBlackTree() + with pytest.raises(Exception): + bst.set_iteration_style("spam") def test_print() -> None: @@ -210,9 +274,6 @@ def test_print() -> None: bst.insert(42) bst.print_tree() - bst.preorder() - bst.inorder() - bst.postorder() def test_elaborate_delete() -> None: