Skip to content

Commit a4bd84c

Browse files
committed
Add get_index utility function
1 parent 122ecb6 commit a4bd84c

File tree

4 files changed

+107
-2
lines changed

4 files changed

+107
-2
lines changed

.github/workflows/build.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ jobs:
2626
run: python -m pip install --upgrade pip setuptools wheel
2727
- name: Install package
2828
run: pip install .[dev]
29+
- name: Check black version
30+
run: black --version
2931
- name: Run black
30-
run: black --check .
32+
run: black -v --check .
3133
- name: Run flake8
3234
run: flake8 .
3335
- name: Run isort

binarytree/__init__.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"heap",
66
"build",
77
"build2",
8+
"get_index",
89
"get_parent",
910
"__version__",
1011
]
@@ -2026,6 +2027,75 @@ def _get_tree_properties(root: Node) -> NodeProperties:
20262027
)
20272028

20282029

2030+
def get_index(root: Node, descendent: Node) -> int:
2031+
"""Return the level-order_ index given the root and a possible descendent.
2032+
2033+
.. _level-order:
2034+
https://en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search
2035+
2036+
:return: Level-order index of the descendent relative to the root node.
2037+
:rtype: int
2038+
:raise binarytree.exceptions.NodeTypeError: If root or descendent is
2039+
not an instance of :class:`binarytree.Node`.
2040+
:raise binarytree.exceptions.NodeReferenceError: If given a node that is
2041+
not a root/descendent.
2042+
2043+
**Example**:
2044+
2045+
.. doctest::
2046+
2047+
>>> from binarytree import Node, get_index
2048+
>>>
2049+
>>> root = Node(1)
2050+
>>> root.left = Node(2)
2051+
>>> root.right = Node(3)
2052+
>>> root.left.right = Node(4)
2053+
>>>
2054+
>>> get_index(root, root.left)
2055+
1
2056+
>>> get_index(root, root.right)
2057+
2
2058+
>>> get_index(root, root.left.right)
2059+
4
2060+
>>> get_index(root.left, root.right)
2061+
Traceback (most recent call last):
2062+
...
2063+
NodeReferenceError: given nodes are not in the same tree
2064+
"""
2065+
if root is None:
2066+
raise NodeTypeError("root must be a Node instance")
2067+
2068+
if descendent is None:
2069+
raise NodeTypeError("descendent must be a Node instance")
2070+
2071+
current_nodes: List[Optional[Node]] = [root]
2072+
current_index = 0
2073+
has_more_nodes = True
2074+
2075+
while has_more_nodes:
2076+
has_more_nodes = False
2077+
next_nodes: List[Optional[Node]] = []
2078+
2079+
for node in current_nodes:
2080+
if node is not None and node is descendent:
2081+
return current_index
2082+
2083+
if node is None:
2084+
next_nodes.append(None)
2085+
next_nodes.append(None)
2086+
else:
2087+
next_nodes.append(node.left)
2088+
next_nodes.append(node.right)
2089+
if node.left is not None or node.right is not None:
2090+
has_more_nodes = True
2091+
2092+
current_index += 1
2093+
2094+
current_nodes = next_nodes
2095+
2096+
raise NodeReferenceError("given nodes are not in the same tree")
2097+
2098+
20292099
def get_parent(root: Node, child: Node) -> Optional[Node]:
20302100
"""Search the binary tree and return the parent of given child.
20312101

docs/specs.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ Function: binarytree.heap
4646

4747
.. autofunction:: binarytree.heap
4848

49+
Function: binarytree.get_index
50+
===============================
51+
52+
.. autofunction:: binarytree.get_index
53+
4954
Function: binarytree.get_parent
5055
===============================
5156

tests/test_tree.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import pytest
77

8-
from binarytree import Node, bst, build, build2, get_parent, heap, tree
8+
from binarytree import Node, bst, build, build2, get_index, get_parent, heap, tree
99
from binarytree.exceptions import (
1010
NodeIndexError,
1111
NodeModifyError,
@@ -1059,6 +1059,34 @@ def test_heap_float_values():
10591059
assert root.size == root_copy.size
10601060

10611061

1062+
def test_get_index():
1063+
root = Node(0)
1064+
root.left = Node(1)
1065+
root.right = Node(2)
1066+
root.left.left = Node(3)
1067+
root.right.right = Node(4)
1068+
1069+
assert get_index(root, root) == 0
1070+
assert get_index(root, root.left) == 1
1071+
assert get_index(root, root.right) == 2
1072+
assert get_index(root, root.left.left) == 3
1073+
assert get_index(root, root.right.right) == 6
1074+
1075+
with pytest.raises(NodeReferenceError) as err:
1076+
get_index(root.left, root.right)
1077+
assert str(err.value) == "given nodes are not in the same tree"
1078+
1079+
with pytest.raises(NodeTypeError) as err:
1080+
# noinspection PyTypeChecker
1081+
get_index(root, None)
1082+
assert str(err.value) == "descendent must be a Node instance"
1083+
1084+
with pytest.raises(NodeTypeError) as err:
1085+
# noinspection PyTypeChecker
1086+
get_index(None, root.left)
1087+
assert str(err.value) == "root must be a Node instance"
1088+
1089+
10621090
# noinspection PyTypeChecker
10631091
def test_get_parent():
10641092
root = Node(0)

0 commit comments

Comments
 (0)