avltree

Stable Version Build Status Documentation Status Checked with mypy Linting: Ruff

This package is a lightweight, pure-Python implementation of the AVL tree. AVL trees are simple self-balancing binary search trees, giving them both amortized and worst-case time complexities of O[log(n)] for insertion, deletion, and retrieval. More reference can be found on the AVL tree Wikipedia page.

PyPI
Source
Documentation

Installation

This package is available on PyPI and can be installed with pip:

pip install avltree
 1# Copyright 2023 Charles Andrews
 2# fmt: off
 3"""[![Stable Version](https://img.shields.io/pypi/v/avltree?color=blue)](https://pypi.org/project/avltree/)
 4[![Build Status](https://github.com/cfandrews/PythonAvlTree/actions/workflows/build.yml/badge.svg)](https://github.com/cfandrews/PythonAvlTree/actions)
 5[![Documentation Status](https://github.com/cfandrews/PythonAvlTree/actions/workflows/documentation.yml/badge.svg)](https://github.com/cfandrews/PythonAvlTree/actions)
 6[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
 7[![Linting: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
 8
 9This package is a lightweight, pure-Python implementation of the AVL tree. AVL trees are
10simple self-balancing binary search trees, giving them both amortized and worst-case
11time complexities of O[log(n)] for insertion, deletion, and retrieval. More reference
12can be found on the [AVL tree Wikipedia page](https://en.wikipedia.org/wiki/AVL_tree).
13
14[PyPI](https://pypi.org/project/avltree/)  
15[Source](https://github.com/cfandrews/PythonAvlTree)  
16[Documentation](https://cfandrews.github.io/PythonAvlTree/avltree.html)
17
18## Installation
19This package is available on PyPI and can be installed with pip:
20```shell
21pip install avltree
22```
23"""  # noqa: W291, D205, D415
24# fmt: on
25from __future__ import annotations
26
27from typing import Final
28
29from ._avl_tree import AvlTree
30
31__all__: Final[list[str]] = ["AvlTree"]
32__docformat__: Final[str] = "google"
class AvlTree(typing.MutableMapping[~_K, ~_V]):
 15class AvlTree(MutableMapping[_K, _V]):
 16    """Lightweight, pure-python AVL tree.
 17
 18    This class implements the MutableMapping interface and can be used in almost every
 19    way that a built-in dictionary can.
 20
 21    # Sorting
 22    The AVL tree data structure makes it possible to easily iterate on keys in
 23    sort-order, and any method which returns an iterable of keys, values, or items will
 24    return them in sort-order by key.
 25
 26    # Keys
 27    Anything used as a key in an AvlTree must implement `__eq__`, `__hash__`, and
 28    `__lt__`. That is to say they must be immutable and have a less-than comparison.
 29
 30    It's recommended to only insert keys which are all the same type, ensuring that they
 31    have a well-ordering. However, keys of different types can be inserted as long as
 32    their `__eq__` and `__lt__` methods behave.
 33
 34    This class provides no protections against poorly behaved keys and can fail in an
 35    undefined manner if keys are not implemented properly.
 36
 37    # Values
 38    Values can be any Python object.
 39
 40    # Recursion
 41    This class does not use recursive techniques. This ensures that this package can be
 42    used on platforms with low recursion limits, even in scenarios with very large and
 43    very deep trees.
 44
 45    # Time Complexities
 46    Time complexities for specific methods can be found in their docstrings.
 47    """
 48
 49    def __init__(self, mapping: Mapping[_K, _V] | None = None) -> None:
 50        """Constructor.
 51
 52        Inserting the elements of a passed-in mapping has an amortized and worst-case
 53        time complexity of O[n*log(n)].
 54
 55        Example usage:
 56
 57        >>> from avltree import AvlTree
 58        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
 59        >>> avl_tree
 60        AvlTree({0: 'a', 1: 'b'})
 61
 62        Args:
 63            mapping (dict[_K, _V] | None): An optional initial mapping of items to add
 64                to this tree. Defaults to None.
 65        """
 66        self.__nodes: Final[dict[_K, AvlTreeNode[_K, _V]]] = {}
 67        self.__root_key: _K | None = None
 68        if mapping is not None:
 69            for key, value in mapping.items():
 70                self[key] = value
 71
 72    def __setitem__(self, __k: _K, __v: _V) -> None:
 73        """Maps the given key to the given value in this tree.
 74
 75        This method has an amortized and worst-case time complexity of O[log(n)].
 76
 77        Example usage:
 78
 79        >>> from avltree import AvlTree
 80        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
 81        >>> avl_tree[0] = 'foo'
 82        >>> avl_tree[2] = 'c'
 83        >>> avl_tree
 84        AvlTree({0: 'foo', 1: 'b', 2: 'c'})
 85
 86        Args:
 87            __k (_K): The key to map.
 88            __v (_V): The value to associate with the key.
 89        """
 90        if __k in self.__nodes:
 91            self.__nodes[__k].value = __v
 92            return
 93        if self.__root_key is None:
 94            self.__root_key = __k
 95            self.__nodes[self.__root_key] = AvlTreeNode[_K, _V](value=__v)
 96            return
 97        stack: Final[list[_K]] = [self.__root_key]
 98        current_node: AvlTreeNode[_K, _V] = self.__nodes[stack[-1]]
 99        while True:
100            if __k < stack[-1] and current_node.lesser_child_key is None:
101                current_node.lesser_child_key = __k
102                self.__nodes[__k] = AvlTreeNode[_K, _V](value=__v)
103                break
104            if stack[-1] < __k and current_node.greater_child_key is None:
105                current_node.greater_child_key = __k
106                self.__nodes[__k] = AvlTreeNode[_K, _V](value=__v)
107                break
108            if __k < stack[-1] and current_node.lesser_child_key is not None:
109                stack.append(current_node.lesser_child_key)
110                current_node = self.__nodes[stack[-1]]
111            elif current_node.greater_child_key is not None:
112                stack.append(current_node.greater_child_key)
113                current_node = self.__nodes[stack[-1]]
114        self.__enforce_avl(stack=stack)
115
116    def __delitem__(self, __k: _K) -> None:  # noqa: C901, PLR0912
117        """Deletes the given key from this tree.
118
119        This method has an amortized and worst-case time complexity of O[log(n)].
120
121        Example usage:
122
123        >>> from avltree import AvlTree
124        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
125        >>> del avl_tree[0]
126        >>> avl_tree
127        AvlTree({1: 'b'})
128
129        Args:
130            __k (_K): The key to delete from this tree.
131
132        Raises:
133            KeyError: If the given key is not present in this tree.
134        """
135        if self.__root_key is None:
136            message: str = f"Key not present in AvlTree: {__k!r}"
137            raise KeyError(message)
138
139        # Find the node to discard and its parent node
140        parent: AvlTreeNode[_K, _V] | None = None
141        node_key: _K = self.__root_key
142        stack: Final[list[_K]] = [node_key]
143        node: AvlTreeNode[_K, _V] = self.__nodes[node_key]
144        while node_key != __k:
145            parent = node
146            node_key = self.__get_closer_key(key=__k, current_key=node_key)
147            stack.append(node_key)
148            node = self.__nodes[node_key]
149
150        # Find the key of the node with which to replace the existing node
151        replacement_key: _K | None = None
152        if node.lesser_child_key is not None and node.greater_child_key is None:
153            replacement_key = node.lesser_child_key
154        elif node.lesser_child_key is None and node.greater_child_key is not None:
155            replacement_key = node.greater_child_key
156        elif node.lesser_child_key is not None and node.greater_child_key is not None:
157            # Find the next highest node than the one to remove
158            successor_parent: AvlTreeNode[_K, _V] | None = None
159            replacement_key = node.greater_child_key
160            successor: AvlTreeNode[_K, _V] = self.__nodes[replacement_key]
161            while successor.lesser_child_key is not None:
162                successor_parent = successor
163                stack.append(replacement_key)
164                replacement_key = successor.lesser_child_key
165                successor = self.__nodes[successor.lesser_child_key]
166
167            # Swap the successor node with the node to replace
168            if successor_parent is not None and successor.greater_child_key is None:
169                successor_parent.lesser_child_key = None
170                successor.greater_child_key = node.greater_child_key
171            elif successor_parent is not None:
172                successor_parent.lesser_child_key = successor.greater_child_key
173                successor.greater_child_key = node.greater_child_key
174            successor.lesser_child_key = node.lesser_child_key
175
176        # Swap the node to its replacement
177        if parent is None:
178            self.__root_key = replacement_key
179        elif parent.lesser_child_key == node_key:
180            parent.lesser_child_key = replacement_key
181        else:
182            parent.greater_child_key = replacement_key
183        del self.__nodes[node_key]
184        if replacement_key is None:
185            stack.remove(node_key)
186        else:
187            stack[stack.index(node_key)] = replacement_key
188        self.__enforce_avl(stack=stack)
189
190    def __getitem__(self, __k: _K) -> _V:
191        """Gets the value associated with the given key.
192
193        This method has an amortized and worst-case time complexity of O[log(n)].
194
195        Example usage:
196
197        >>> from avltree import AvlTree
198        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
199        >>> avl_tree[0]
200        'a'
201        >>> avl_tree[2]
202        KeyError: 'Key not present in AvlTree: 2'
203
204        Args:
205            __k (_K): The key.
206
207        Returns:
208            _V: The value associated with the given key.
209
210        Raises:
211            KeyError: If the given key is not present in this tree.
212        """
213        if self.__root_key is None:
214            message: str = f"Key not present in AvlTree: {__k!r}"
215            raise KeyError(message)
216        current_key: _K = self.__root_key
217        current_node: AvlTreeNode[_K, _V] = self.__nodes[current_key]
218        while current_key != __k:
219            current_key = self.__get_closer_key(key=__k, current_key=current_key)
220            current_node = self.__nodes[current_key]
221        return current_node.value
222
223    def __len__(self) -> int:
224        """Returns the number of items contained in this tree.
225
226        This method has an amortized and worst-case time complexity of O[1].
227
228        Example usage:
229
230        >>> from avltree import AvlTree
231        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
232        >>> len(avl_tree)
233        2
234
235        Returns:
236            int: The number of items contained in this tree.
237        """
238        return len(self.__nodes)
239
240    def __iter__(self) -> Iterator[_K]:
241        """Iterates over all keys contained in this tree in sort order.
242
243        Getting the first key has an amortized and worst-case time complexity of
244        O[log(n)]. Iterating over all keys has an amortized and worst-case time
245        complexity of O[n].
246
247        Example usage:
248
249        >>> from avltree import AvlTree
250        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
251        >>> for key in avl_tree:
252        ...     print(key)
253        ...
254        0
255        1
256
257        Returns:
258            Iterator[_K]: The iterator object.
259        """
260        return self.between()
261
262    def __repr__(self) -> str:
263        """Builds a developer-friendly string representation of this AvlTree.
264
265        Example usage:
266
267        >>> from avltree import AvlTree
268        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
269        >>> repr(avl_tree)
270        "AvlTree({0: 'a', 1: 'b'})"
271
272
273        Returns:
274            str: A string representation of this AvlTree.
275        """
276        return f"AvlTree({{{', '.join(f'{k!r}: {v!r}' for k, v in self.items())}}})"
277
278    def minimum(self) -> _K:
279        """Gets the minimum key contained in this tree.
280
281        This method has an amortized and worst-case time complexity of O[log(n)].
282
283        Example usage:
284
285        >>> from avltree import AvlTree
286        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
287        >>> avl_tree.minimum()
288        0
289
290        Returns:
291            _K: The minimum key.
292
293        Raises:
294            ValueError: If there are no keys present in this tree.
295        """
296        if self.__root_key is None:
297            message: Final[str] = "Cannot get the minimum of an empty AvlTree"
298            raise ValueError(message)
299        key: _K = self.__root_key
300        while self.__nodes[key].lesser_child_key is not None:
301            key = self.__nodes[key].lesser_child_key  # type: ignore[assignment]
302        return key
303
304    def maximum(self) -> _K:
305        """Gets the maximum key contained in this tree.
306
307        This method has an amortized and worst-case time complexity of O[log(n)].
308
309        Example usage:
310
311        >>> from avltree import AvlTree
312        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
313        >>> avl_tree.maximum()
314        1
315
316        Returns:
317            _K: The maximum key.
318
319        Raises:
320            ValueError: If there are no keys present in this tree.
321        """
322        if self.__root_key is None:
323            message: Final[str] = "Cannot get the maximum of an empty AvlTree"
324            raise ValueError(message)
325        key: _K = self.__root_key
326        node: AvlTreeNode[_K, _V] = self.__nodes[key]
327        while node.greater_child_key is not None:
328            key = node.greater_child_key
329            node = self.__nodes[key]
330        return key
331
332    def between(  # noqa: C901, PLR0912
333        self,
334        start: _K | None = None,
335        stop: _K | None = None,
336        treatment: Literal["inclusive", "exclusive"] = "inclusive",
337    ) -> Iterator[_K]:
338        """Iterates over all keys between the given start and stop in sort order.
339
340        Getting the first key has an amortized and worst-case time complexity of
341        O[log(n)]. Iterating over all keys has an amortized and worst-case time
342        complexity of O[k], where k is the number of items in only the interval between
343        start and stop.
344
345        Example usage:
346
347        >>> from avltree import AvlTree
348        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b', 2: 'c'})
349        >>> for key in avl_tree.between(start=0, stop=2, treatment="inclusive"):
350        ...     print(key)
351        ...
352        0
353        1
354        2
355        >>> for key in avl_tree.between(start=0, treatment="exclusive"):
356        ...     print(key)
357        ...
358        1
359        2
360
361        Args:
362            start (_K | None): The key at which to start iterating. If None, iteration
363                starts at the minimum key. Defaults to None.
364            stop (_K | None): The key at which to stop iterating. If None, iteration
365                stops at the maximum key. Defaults to None.
366            treatment (Literal["inclusive", "exclusive"]): Whether the given start and
367                stop should be included or excluded from the returned iterator. Has no
368                effect when start and stop are None. Defaults to "inclusive".
369
370        Returns:
371            Iterator[_K]: An iterator which will iterate over all keys between the given
372                start and stop.
373        """
374        if self.__root_key is None:
375            return
376        stack: Final[list[tuple[_K, bool]]] = []
377        current_key: _K = self.__root_key
378        while True:
379            current_node: AvlTreeNode[_K, _V] = self.__nodes[current_key]
380            if start is None or start < current_key:
381                stack.append((current_key, True))
382                if current_node.lesser_child_key is not None:
383                    current_key = current_node.lesser_child_key
384                else:
385                    break
386            elif start == current_key:
387                if treatment == "inclusive":
388                    stack.append((current_key, True))
389                    break
390                elif current_node.greater_child_key is not None:
391                    current_key = current_node.greater_child_key
392                else:
393                    break
394            elif current_node.greater_child_key is not None:
395                current_key = current_node.greater_child_key
396            else:
397                break
398        while len(stack) > 0:
399            key, lesser_child_visited = stack.pop(-1)
400            node: AvlTreeNode[_K, _V] = self.__nodes[key]
401            if (
402                stop is not None  # noqa: PLR0916
403                and (stop < key or (stop == key and treatment == "exclusive"))
404                and (lesser_child_visited or node.lesser_child_key is None)
405            ):
406                break
407            elif node.lesser_child_key is not None and not lesser_child_visited:
408                stack.append((key, True))
409                stack.append((node.lesser_child_key, False))
410            elif node.greater_child_key is not None:
411                stack.append((node.greater_child_key, False))
412                yield key
413            else:
414                yield key
415
416    def __get_closer_key(self, key: _K, current_key: _K) -> _K:
417        """Gets the next closest key to the given key.
418
419        Args:
420            key (_K): The key to search for.
421            current_key (_K): The current key.
422
423        Returns:
424            _K: The next closest key to the given key.
425
426        Raises:
427            KeyError: If the given key is not present in this tree.
428        """
429        current_node: Final[AvlTreeNode[_K, _V]] = self.__nodes[current_key]
430        if key < current_key and current_node.lesser_child_key is not None:
431            return current_node.lesser_child_key
432        if current_key < key and current_node.greater_child_key is not None:
433            return current_node.greater_child_key
434        message: Final[str] = f"Key not present in AvlTree: {key!r}"
435        raise KeyError(message)
436
437    def __enforce_avl(self, stack: list[_K]) -> None:
438        """Enforces the AVL property on this tree.
439
440        Args:
441            stack (list[_K]): The stack to traverse in reverse order.
442        """
443        while len(stack) > 0:
444            key: _K = stack.pop(-1)
445            node: AvlTreeNode[_K, _V] = self.__nodes[key]
446            balance: int = self.__calculate_balance(node=node)
447            if -1 <= balance <= 1:
448                self.__update_height(node=node)
449                continue
450            if balance == -2:  # noqa: PLR2004
451                lesser_child_key: _K = cast(_K, node.lesser_child_key)
452                if self.__calculate_balance(node=self.__nodes[lesser_child_key]) == 1:
453                    node.lesser_child_key = self.__rotate(
454                        key=lesser_child_key,
455                        direction="left",
456                    )
457                replacement_key: _K = self.__rotate(key=key, direction="right")
458            else:
459                greater_child_key: _K = cast(_K, node.greater_child_key)
460                if self.__calculate_balance(node=self.__nodes[greater_child_key]) == -1:
461                    node.greater_child_key = self.__rotate(
462                        key=greater_child_key,
463                        direction="right",
464                    )
465                replacement_key = self.__rotate(key=key, direction="left")
466            parent_node: AvlTreeNode[_K, _V] | None = (
467                None if len(stack) == 0 else self.__nodes[stack[-1]]
468            )
469            if parent_node is None:
470                self.__root_key = replacement_key
471            elif parent_node.lesser_child_key == key:
472                parent_node.lesser_child_key = replacement_key
473            else:
474                parent_node.greater_child_key = replacement_key
475
476    def __calculate_balance(self, node: AvlTreeNode[_K, _V]) -> int:
477        """Calculates the balance factor of the given node.
478
479        Args:
480            node (AvlTreeNode[_K, _V]): The node.
481
482        Returns:
483            int: The balance factor of the given node.
484        """
485        return (
486            -1
487            if node.greater_child_key is None
488            else self.__nodes[node.greater_child_key].height
489        ) - (
490            -1
491            if node.lesser_child_key is None
492            else self.__nodes[node.lesser_child_key].height
493        )
494
495    def __update_height(self, node: AvlTreeNode[_K, _V]) -> None:
496        """Updates the height of the given node.
497
498        Args:
499            node (AvlTreeNode[_K, _V]): The node.
500        """
501        node.height = 1 + max(
502            (
503                -1
504                if node.greater_child_key is None
505                else self.__nodes[node.greater_child_key].height
506            ),
507            (
508                -1
509                if node.lesser_child_key is None
510                else self.__nodes[node.lesser_child_key].height
511            ),
512        )
513
514    def __rotate(self, key: _K, direction: Literal["left", "right"]) -> _K:
515        """Performs a rotation at the given key.
516
517        Args:
518            key (_K): The key to perform a right rotation on.
519            direction (Literal["left", "right"]): The direction of the rotation.
520
521        Returns:
522            _K: The new root key of this subtree.
523
524        Raises:
525            ValueError: If the shape of the tree is incompatible with the requested
526                rotation direction.
527        """
528        node: Final[AvlTreeNode[_K, _V]] = self.__nodes[key]
529        replacement_key: Final[_K] = cast(
530            _K,
531            node.greater_child_key if direction == "left" else node.lesser_child_key,
532        )
533        replacement_node: Final[AvlTreeNode[_K, _V]] = self.__nodes[replacement_key]
534        if direction == "left":
535            node.greater_child_key = replacement_node.lesser_child_key
536            replacement_node.lesser_child_key = key
537        else:
538            node.lesser_child_key = replacement_node.greater_child_key
539            replacement_node.greater_child_key = key
540        self.__update_height(node=node)
541        self.__update_height(node=replacement_node)
542        return replacement_key

Lightweight, pure-python AVL tree.

This class implements the MutableMapping interface and can be used in almost every way that a built-in dictionary can.

Sorting

The AVL tree data structure makes it possible to easily iterate on keys in sort-order, and any method which returns an iterable of keys, values, or items will return them in sort-order by key.

Keys

Anything used as a key in an AvlTree must implement __eq__, __hash__, and __lt__. That is to say they must be immutable and have a less-than comparison.

It's recommended to only insert keys which are all the same type, ensuring that they have a well-ordering. However, keys of different types can be inserted as long as their __eq__ and __lt__ methods behave.

This class provides no protections against poorly behaved keys and can fail in an undefined manner if keys are not implemented properly.

Values

Values can be any Python object.

Recursion

This class does not use recursive techniques. This ensures that this package can be used on platforms with low recursion limits, even in scenarios with very large and very deep trees.

Time Complexities

Time complexities for specific methods can be found in their docstrings.

AvlTree(mapping: Optional[Mapping[~_K, ~_V]] = None)
49    def __init__(self, mapping: Mapping[_K, _V] | None = None) -> None:
50        """Constructor.
51
52        Inserting the elements of a passed-in mapping has an amortized and worst-case
53        time complexity of O[n*log(n)].
54
55        Example usage:
56
57        >>> from avltree import AvlTree
58        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
59        >>> avl_tree
60        AvlTree({0: 'a', 1: 'b'})
61
62        Args:
63            mapping (dict[_K, _V] | None): An optional initial mapping of items to add
64                to this tree. Defaults to None.
65        """
66        self.__nodes: Final[dict[_K, AvlTreeNode[_K, _V]]] = {}
67        self.__root_key: _K | None = None
68        if mapping is not None:
69            for key, value in mapping.items():
70                self[key] = value

Constructor.

Inserting the elements of a passed-in mapping has an amortized and worst-case time complexity of O[n*log(n)].

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
>>> avl_tree
AvlTree({0: 'a', 1: 'b'})
Arguments:
  • mapping (dict[_K, _V] | None): An optional initial mapping of items to add to this tree. Defaults to None.
def __setitem__(self, _AvlTree__k: ~_K, _AvlTree__v: ~_V) -> None:
 72    def __setitem__(self, __k: _K, __v: _V) -> None:
 73        """Maps the given key to the given value in this tree.
 74
 75        This method has an amortized and worst-case time complexity of O[log(n)].
 76
 77        Example usage:
 78
 79        >>> from avltree import AvlTree
 80        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
 81        >>> avl_tree[0] = 'foo'
 82        >>> avl_tree[2] = 'c'
 83        >>> avl_tree
 84        AvlTree({0: 'foo', 1: 'b', 2: 'c'})
 85
 86        Args:
 87            __k (_K): The key to map.
 88            __v (_V): The value to associate with the key.
 89        """
 90        if __k in self.__nodes:
 91            self.__nodes[__k].value = __v
 92            return
 93        if self.__root_key is None:
 94            self.__root_key = __k
 95            self.__nodes[self.__root_key] = AvlTreeNode[_K, _V](value=__v)
 96            return
 97        stack: Final[list[_K]] = [self.__root_key]
 98        current_node: AvlTreeNode[_K, _V] = self.__nodes[stack[-1]]
 99        while True:
100            if __k < stack[-1] and current_node.lesser_child_key is None:
101                current_node.lesser_child_key = __k
102                self.__nodes[__k] = AvlTreeNode[_K, _V](value=__v)
103                break
104            if stack[-1] < __k and current_node.greater_child_key is None:
105                current_node.greater_child_key = __k
106                self.__nodes[__k] = AvlTreeNode[_K, _V](value=__v)
107                break
108            if __k < stack[-1] and current_node.lesser_child_key is not None:
109                stack.append(current_node.lesser_child_key)
110                current_node = self.__nodes[stack[-1]]
111            elif current_node.greater_child_key is not None:
112                stack.append(current_node.greater_child_key)
113                current_node = self.__nodes[stack[-1]]
114        self.__enforce_avl(stack=stack)

Maps the given key to the given value in this tree.

This method has an amortized and worst-case time complexity of O[log(n)].

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
>>> avl_tree[0] = 'foo'
>>> avl_tree[2] = 'c'
>>> avl_tree
AvlTree({0: 'foo', 1: 'b', 2: 'c'})
Arguments:
  • __k (_K): The key to map.
  • __v (_V): The value to associate with the key.
def __delitem__(self, _AvlTree__k: ~_K) -> None:
116    def __delitem__(self, __k: _K) -> None:  # noqa: C901, PLR0912
117        """Deletes the given key from this tree.
118
119        This method has an amortized and worst-case time complexity of O[log(n)].
120
121        Example usage:
122
123        >>> from avltree import AvlTree
124        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
125        >>> del avl_tree[0]
126        >>> avl_tree
127        AvlTree({1: 'b'})
128
129        Args:
130            __k (_K): The key to delete from this tree.
131
132        Raises:
133            KeyError: If the given key is not present in this tree.
134        """
135        if self.__root_key is None:
136            message: str = f"Key not present in AvlTree: {__k!r}"
137            raise KeyError(message)
138
139        # Find the node to discard and its parent node
140        parent: AvlTreeNode[_K, _V] | None = None
141        node_key: _K = self.__root_key
142        stack: Final[list[_K]] = [node_key]
143        node: AvlTreeNode[_K, _V] = self.__nodes[node_key]
144        while node_key != __k:
145            parent = node
146            node_key = self.__get_closer_key(key=__k, current_key=node_key)
147            stack.append(node_key)
148            node = self.__nodes[node_key]
149
150        # Find the key of the node with which to replace the existing node
151        replacement_key: _K | None = None
152        if node.lesser_child_key is not None and node.greater_child_key is None:
153            replacement_key = node.lesser_child_key
154        elif node.lesser_child_key is None and node.greater_child_key is not None:
155            replacement_key = node.greater_child_key
156        elif node.lesser_child_key is not None and node.greater_child_key is not None:
157            # Find the next highest node than the one to remove
158            successor_parent: AvlTreeNode[_K, _V] | None = None
159            replacement_key = node.greater_child_key
160            successor: AvlTreeNode[_K, _V] = self.__nodes[replacement_key]
161            while successor.lesser_child_key is not None:
162                successor_parent = successor
163                stack.append(replacement_key)
164                replacement_key = successor.lesser_child_key
165                successor = self.__nodes[successor.lesser_child_key]
166
167            # Swap the successor node with the node to replace
168            if successor_parent is not None and successor.greater_child_key is None:
169                successor_parent.lesser_child_key = None
170                successor.greater_child_key = node.greater_child_key
171            elif successor_parent is not None:
172                successor_parent.lesser_child_key = successor.greater_child_key
173                successor.greater_child_key = node.greater_child_key
174            successor.lesser_child_key = node.lesser_child_key
175
176        # Swap the node to its replacement
177        if parent is None:
178            self.__root_key = replacement_key
179        elif parent.lesser_child_key == node_key:
180            parent.lesser_child_key = replacement_key
181        else:
182            parent.greater_child_key = replacement_key
183        del self.__nodes[node_key]
184        if replacement_key is None:
185            stack.remove(node_key)
186        else:
187            stack[stack.index(node_key)] = replacement_key
188        self.__enforce_avl(stack=stack)

Deletes the given key from this tree.

This method has an amortized and worst-case time complexity of O[log(n)].

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
>>> del avl_tree[0]
>>> avl_tree
AvlTree({1: 'b'})
Arguments:
  • __k (_K): The key to delete from this tree.
Raises:
  • KeyError: If the given key is not present in this tree.
def __getitem__(self, _AvlTree__k: ~_K) -> ~_V:
190    def __getitem__(self, __k: _K) -> _V:
191        """Gets the value associated with the given key.
192
193        This method has an amortized and worst-case time complexity of O[log(n)].
194
195        Example usage:
196
197        >>> from avltree import AvlTree
198        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
199        >>> avl_tree[0]
200        'a'
201        >>> avl_tree[2]
202        KeyError: 'Key not present in AvlTree: 2'
203
204        Args:
205            __k (_K): The key.
206
207        Returns:
208            _V: The value associated with the given key.
209
210        Raises:
211            KeyError: If the given key is not present in this tree.
212        """
213        if self.__root_key is None:
214            message: str = f"Key not present in AvlTree: {__k!r}"
215            raise KeyError(message)
216        current_key: _K = self.__root_key
217        current_node: AvlTreeNode[_K, _V] = self.__nodes[current_key]
218        while current_key != __k:
219            current_key = self.__get_closer_key(key=__k, current_key=current_key)
220            current_node = self.__nodes[current_key]
221        return current_node.value

Gets the value associated with the given key.

This method has an amortized and worst-case time complexity of O[log(n)].

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
>>> avl_tree[0]
'a'
>>> avl_tree[2]
KeyError: 'Key not present in AvlTree: 2'
Arguments:
  • __k (_K): The key.
Returns:

_V: The value associated with the given key.

Raises:
  • KeyError: If the given key is not present in this tree.
def __len__(self) -> int:
223    def __len__(self) -> int:
224        """Returns the number of items contained in this tree.
225
226        This method has an amortized and worst-case time complexity of O[1].
227
228        Example usage:
229
230        >>> from avltree import AvlTree
231        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
232        >>> len(avl_tree)
233        2
234
235        Returns:
236            int: The number of items contained in this tree.
237        """
238        return len(self.__nodes)

Returns the number of items contained in this tree.

This method has an amortized and worst-case time complexity of O[1].

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
>>> len(avl_tree)
2
Returns:

int: The number of items contained in this tree.

def __iter__(self) -> Iterator[~_K]:
240    def __iter__(self) -> Iterator[_K]:
241        """Iterates over all keys contained in this tree in sort order.
242
243        Getting the first key has an amortized and worst-case time complexity of
244        O[log(n)]. Iterating over all keys has an amortized and worst-case time
245        complexity of O[n].
246
247        Example usage:
248
249        >>> from avltree import AvlTree
250        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
251        >>> for key in avl_tree:
252        ...     print(key)
253        ...
254        0
255        1
256
257        Returns:
258            Iterator[_K]: The iterator object.
259        """
260        return self.between()

Iterates over all keys contained in this tree in sort order.

Getting the first key has an amortized and worst-case time complexity of O[log(n)]. Iterating over all keys has an amortized and worst-case time complexity of O[n].

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
>>> for key in avl_tree:
...     print(key)
...
0
1
Returns:

Iterator[_K]: The iterator object.

def __repr__(self) -> str:
262    def __repr__(self) -> str:
263        """Builds a developer-friendly string representation of this AvlTree.
264
265        Example usage:
266
267        >>> from avltree import AvlTree
268        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
269        >>> repr(avl_tree)
270        "AvlTree({0: 'a', 1: 'b'})"
271
272
273        Returns:
274            str: A string representation of this AvlTree.
275        """
276        return f"AvlTree({{{', '.join(f'{k!r}: {v!r}' for k, v in self.items())}}})"

Builds a developer-friendly string representation of this AvlTree.

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
>>> repr(avl_tree)
"AvlTree({0: 'a', 1: 'b'})"
Returns:

str: A string representation of this AvlTree.

def minimum(self) -> ~_K:
278    def minimum(self) -> _K:
279        """Gets the minimum key contained in this tree.
280
281        This method has an amortized and worst-case time complexity of O[log(n)].
282
283        Example usage:
284
285        >>> from avltree import AvlTree
286        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
287        >>> avl_tree.minimum()
288        0
289
290        Returns:
291            _K: The minimum key.
292
293        Raises:
294            ValueError: If there are no keys present in this tree.
295        """
296        if self.__root_key is None:
297            message: Final[str] = "Cannot get the minimum of an empty AvlTree"
298            raise ValueError(message)
299        key: _K = self.__root_key
300        while self.__nodes[key].lesser_child_key is not None:
301            key = self.__nodes[key].lesser_child_key  # type: ignore[assignment]
302        return key

Gets the minimum key contained in this tree.

This method has an amortized and worst-case time complexity of O[log(n)].

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
>>> avl_tree.minimum()
0
Returns:

_K: The minimum key.

Raises:
  • ValueError: If there are no keys present in this tree.
def maximum(self) -> ~_K:
304    def maximum(self) -> _K:
305        """Gets the maximum key contained in this tree.
306
307        This method has an amortized and worst-case time complexity of O[log(n)].
308
309        Example usage:
310
311        >>> from avltree import AvlTree
312        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
313        >>> avl_tree.maximum()
314        1
315
316        Returns:
317            _K: The maximum key.
318
319        Raises:
320            ValueError: If there are no keys present in this tree.
321        """
322        if self.__root_key is None:
323            message: Final[str] = "Cannot get the maximum of an empty AvlTree"
324            raise ValueError(message)
325        key: _K = self.__root_key
326        node: AvlTreeNode[_K, _V] = self.__nodes[key]
327        while node.greater_child_key is not None:
328            key = node.greater_child_key
329            node = self.__nodes[key]
330        return key

Gets the maximum key contained in this tree.

This method has an amortized and worst-case time complexity of O[log(n)].

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b'})
>>> avl_tree.maximum()
1
Returns:

_K: The maximum key.

Raises:
  • ValueError: If there are no keys present in this tree.
def between( self, start: Optional[~_K] = None, stop: Optional[~_K] = None, treatment: Literal['inclusive', 'exclusive'] = 'inclusive') -> Iterator[~_K]:
332    def between(  # noqa: C901, PLR0912
333        self,
334        start: _K | None = None,
335        stop: _K | None = None,
336        treatment: Literal["inclusive", "exclusive"] = "inclusive",
337    ) -> Iterator[_K]:
338        """Iterates over all keys between the given start and stop in sort order.
339
340        Getting the first key has an amortized and worst-case time complexity of
341        O[log(n)]. Iterating over all keys has an amortized and worst-case time
342        complexity of O[k], where k is the number of items in only the interval between
343        start and stop.
344
345        Example usage:
346
347        >>> from avltree import AvlTree
348        >>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b', 2: 'c'})
349        >>> for key in avl_tree.between(start=0, stop=2, treatment="inclusive"):
350        ...     print(key)
351        ...
352        0
353        1
354        2
355        >>> for key in avl_tree.between(start=0, treatment="exclusive"):
356        ...     print(key)
357        ...
358        1
359        2
360
361        Args:
362            start (_K | None): The key at which to start iterating. If None, iteration
363                starts at the minimum key. Defaults to None.
364            stop (_K | None): The key at which to stop iterating. If None, iteration
365                stops at the maximum key. Defaults to None.
366            treatment (Literal["inclusive", "exclusive"]): Whether the given start and
367                stop should be included or excluded from the returned iterator. Has no
368                effect when start and stop are None. Defaults to "inclusive".
369
370        Returns:
371            Iterator[_K]: An iterator which will iterate over all keys between the given
372                start and stop.
373        """
374        if self.__root_key is None:
375            return
376        stack: Final[list[tuple[_K, bool]]] = []
377        current_key: _K = self.__root_key
378        while True:
379            current_node: AvlTreeNode[_K, _V] = self.__nodes[current_key]
380            if start is None or start < current_key:
381                stack.append((current_key, True))
382                if current_node.lesser_child_key is not None:
383                    current_key = current_node.lesser_child_key
384                else:
385                    break
386            elif start == current_key:
387                if treatment == "inclusive":
388                    stack.append((current_key, True))
389                    break
390                elif current_node.greater_child_key is not None:
391                    current_key = current_node.greater_child_key
392                else:
393                    break
394            elif current_node.greater_child_key is not None:
395                current_key = current_node.greater_child_key
396            else:
397                break
398        while len(stack) > 0:
399            key, lesser_child_visited = stack.pop(-1)
400            node: AvlTreeNode[_K, _V] = self.__nodes[key]
401            if (
402                stop is not None  # noqa: PLR0916
403                and (stop < key or (stop == key and treatment == "exclusive"))
404                and (lesser_child_visited or node.lesser_child_key is None)
405            ):
406                break
407            elif node.lesser_child_key is not None and not lesser_child_visited:
408                stack.append((key, True))
409                stack.append((node.lesser_child_key, False))
410            elif node.greater_child_key is not None:
411                stack.append((node.greater_child_key, False))
412                yield key
413            else:
414                yield key

Iterates over all keys between the given start and stop in sort order.

Getting the first key has an amortized and worst-case time complexity of O[log(n)]. Iterating over all keys has an amortized and worst-case time complexity of O[k], where k is the number of items in only the interval between start and stop.

Example usage:

>>> from avltree import AvlTree
>>> avl_tree = AvlTree[int, str]({0: 'a', 1: 'b', 2: 'c'})
>>> for key in avl_tree.between(start=0, stop=2, treatment="inclusive"):
...     print(key)
...
0
1
2
>>> for key in avl_tree.between(start=0, treatment="exclusive"):
...     print(key)
...
1
2
Arguments:
  • start (_K | None): The key at which to start iterating. If None, iteration starts at the minimum key. Defaults to None.
  • stop (_K | None): The key at which to stop iterating. If None, iteration stops at the maximum key. Defaults to None.
  • treatment (Literal["inclusive", "exclusive"]): Whether the given start and stop should be included or excluded from the returned iterator. Has no effect when start and stop are None. Defaults to "inclusive".
Returns:

Iterator[_K]: An iterator which will iterate over all keys between the given start and stop.

Inherited Members
collections.abc.MutableMapping
pop
popitem
clear
update
setdefault
collections.abc.Mapping
get
keys
items
values