core-data-structures 1.3.0__tar.gz → 1.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/PKG-INFO +3 -2
  2. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/core_data_structures.egg-info/PKG-INFO +3 -2
  3. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/core_data_structures.egg-info/SOURCES.txt +1 -0
  4. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/core_data_structures.egg-info/requires.txt +1 -1
  5. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/__init__.py +1 -5
  6. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/base.py +5 -8
  7. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/graphs/__init__.py +1 -4
  8. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/graphs/graphs.py +28 -28
  9. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/graphs/vertex.py +9 -12
  10. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/lists/base.py +15 -17
  11. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/lists/double_linked_list.py +7 -8
  12. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/lists/singly_linked_list.py +1 -4
  13. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/trees/__init__.py +0 -3
  14. core_data_structures-1.4.1/data_structures/trees/base.py +44 -0
  15. core_data_structures-1.4.1/data_structures/trees/binary_tree.py +247 -0
  16. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/trees/simple_tree.py +2 -6
  17. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/pyproject.toml +3 -2
  18. core_data_structures-1.4.1/setup.py +5 -0
  19. core_data_structures-1.3.0/data_structures/trees/binary_tree.py +0 -230
  20. core_data_structures-1.3.0/setup.py +0 -6
  21. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/LICENSE +0 -0
  22. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/README.rst +0 -0
  23. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/core_data_structures.egg-info/dependency_links.txt +0 -0
  24. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/core_data_structures.egg-info/top_level.txt +0 -0
  25. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/lists/__init__.py +0 -0
  26. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/data_structures/py.typed +0 -0
  27. {core_data_structures-1.3.0 → core_data_structures-1.4.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: core-data-structures
3
- Version: 1.3.0
3
+ Version: 1.4.1
4
4
  Summary: This project/library contains commons data structures...
5
5
  Author-email: Alejandro Cora González <alek.cora.glez@gmail.com>
6
6
  Maintainer: Alejandro Cora González
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Programming Language :: Python :: 3.13
23
23
  Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Programming Language :: Python :: Implementation :: PyPy
25
+ Classifier: Typing :: Typed
25
26
  Requires-Python: >=3.9
26
27
  Description-Content-Type: text/x-rst
27
28
  License-File: LICENSE
@@ -29,7 +30,7 @@ Requires-Dist: core-mixins>=3.2.0
29
30
  Requires-Dist: typing-extensions>=4.8.0
30
31
  Provides-Extra: dev
31
32
  Requires-Dist: core-dev-tools>=2.1.0; extra == "dev"
32
- Requires-Dist: core-tests>=2.1.0; extra == "dev"
33
+ Requires-Dist: core-tests>=2.3.0; extra == "dev"
33
34
  Dynamic: license-file
34
35
 
35
36
  core-data-structures
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: core-data-structures
3
- Version: 1.3.0
3
+ Version: 1.4.1
4
4
  Summary: This project/library contains commons data structures...
5
5
  Author-email: Alejandro Cora González <alek.cora.glez@gmail.com>
6
6
  Maintainer: Alejandro Cora González
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Programming Language :: Python :: 3.13
23
23
  Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Programming Language :: Python :: Implementation :: PyPy
25
+ Classifier: Typing :: Typed
25
26
  Requires-Python: >=3.9
26
27
  Description-Content-Type: text/x-rst
27
28
  License-File: LICENSE
@@ -29,7 +30,7 @@ Requires-Dist: core-mixins>=3.2.0
29
30
  Requires-Dist: typing-extensions>=4.8.0
30
31
  Provides-Extra: dev
31
32
  Requires-Dist: core-dev-tools>=2.1.0; extra == "dev"
32
- Requires-Dist: core-tests>=2.1.0; extra == "dev"
33
+ Requires-Dist: core-tests>=2.3.0; extra == "dev"
33
34
  Dynamic: license-file
34
35
 
35
36
  core-data-structures
@@ -18,5 +18,6 @@ data_structures/lists/base.py
18
18
  data_structures/lists/double_linked_list.py
19
19
  data_structures/lists/singly_linked_list.py
20
20
  data_structures/trees/__init__.py
21
+ data_structures/trees/base.py
21
22
  data_structures/trees/binary_tree.py
22
23
  data_structures/trees/simple_tree.py
@@ -3,4 +3,4 @@ typing-extensions>=4.8.0
3
3
 
4
4
  [dev]
5
5
  core-dev-tools>=2.1.0
6
- core-tests>=2.1.0
6
+ core-tests>=2.3.0
@@ -1,10 +1,6 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Core data structures package."""
4
2
 
5
- from importlib.metadata import PackageNotFoundError
6
- from importlib.metadata import version
7
-
3
+ from importlib.metadata import PackageNotFoundError, version
8
4
 
9
5
  try:
10
6
  __version__ = version("core-data-structures")
@@ -1,11 +1,8 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Base node used as a building block across data structures."""
4
2
 
5
- from typing import Generic
6
- from typing import Optional
7
- from typing import TypeVar
3
+ from __future__ import annotations
8
4
 
5
+ from typing import Generic, TypeVar
9
6
 
10
7
  __all__ = [
11
8
  "Node",
@@ -21,15 +18,15 @@ class Node(Generic[T]): # pylint: disable=too-few-public-methods
21
18
  subclassed by concrete node types.
22
19
  """
23
20
 
24
- def __init__(self, value: Optional[T] = None) -> None:
21
+ def __init__(self, value: T | None = None) -> None:
25
22
  self._value = value
26
23
 
27
24
  def __repr__(self):
28
25
  return f"[{self._value}]"
29
26
 
30
27
  @property
31
- def value(self) -> Optional[T]:
32
- """ The value held by the node. """
28
+ def value(self) -> T | None:
29
+ """The value held by the node."""
33
30
  return self._value
34
31
 
35
32
  @value.setter
@@ -1,10 +1,7 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Graph data structures package."""
4
2
 
5
3
  from .graphs import Graph
6
- from . vertex import Vertex
7
-
4
+ from .vertex import Vertex
8
5
 
9
6
  __all__ = [
10
7
  "Graph",
@@ -1,10 +1,10 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Graph data structure with common traversal and path-finding algorithms."""
4
2
 
3
+ from __future__ import annotations
4
+
5
5
  import json
6
6
  from queue import Queue
7
- from typing import Any, Dict, List, Optional
7
+ from typing import Any
8
8
 
9
9
  from .vertex import Vertex
10
10
 
@@ -29,13 +29,9 @@ class Graph:
29
29
  E --- F
30
30
  """
31
31
 
32
- return cls.build_graph({
33
- "A": ["B", "C"],
34
- "B": ["C", "D"],
35
- "C": ["D"],
36
- "D": ["E", "F"],
37
- "E": ["F"]
38
- })
32
+ return cls.build_graph(
33
+ {"A": ["B", "C"], "B": ["C", "D"], "C": ["D"], "D": ["E", "F"], "E": ["F"]}
34
+ )
39
35
 
40
36
  def __iter__(self):
41
37
  return iter(self.vertices.values())
@@ -44,19 +40,23 @@ class Graph:
44
40
  return key in self.vertices
45
41
 
46
42
  def __str__(self):
47
- return json.dumps({
48
- key: list(vertex.get_connections_ids())
49
- for key, vertex in self.vertices.items()
50
- })
43
+ return json.dumps(
44
+ {
45
+ key: list(vertex.get_connections_ids())
46
+ for key, vertex in self.vertices.items()
47
+ }
48
+ )
51
49
 
52
50
  def __repr__(self):
53
- return json.dumps({
54
- key: list(vertex.get_connections_ids())
55
- for key, vertex in self.vertices.items()
56
- })
51
+ return json.dumps(
52
+ {
53
+ key: list(vertex.get_connections_ids())
54
+ for key, vertex in self.vertices.items()
55
+ }
56
+ )
57
57
 
58
58
  @classmethod
59
- def build_graph(cls, graph_definition: Dict[Any, List]):
59
+ def build_graph(cls, graph_definition: dict[Any, list]):
60
60
  """
61
61
  Create a Graph
62
62
 
@@ -75,23 +75,23 @@ class Graph:
75
75
  return graph
76
76
 
77
77
  def add_vertex(self, key) -> Vertex:
78
- """ Add a vertex for the given key if it does not already exist and return it. """
78
+ """Add a vertex for the given key if it does not already exist and return it."""
79
79
 
80
80
  if key not in self.vertices:
81
81
  self.vertices[key] = Vertex(key)
82
82
 
83
83
  return self.vertices[key]
84
84
 
85
- def get_vertex(self, key) -> Optional[Vertex]:
86
- """ Return a vertex by key """
85
+ def get_vertex(self, key) -> Vertex | None:
86
+ """Return a vertex by key"""
87
87
  return self.vertices.get(key, None)
88
88
 
89
- def get_vertices(self) -> Dict:
90
- """ Return all vertices """
89
+ def get_vertices(self) -> dict:
90
+ """Return all vertices"""
91
91
  return self.vertices
92
92
 
93
93
  def add_edge(self, key_vertex1, key_vertex2, weight=0):
94
- """ Add a directed edge from key_vertex1 to key_vertex2 with an optional weight. """
94
+ """Add a directed edge from key_vertex1 to key_vertex2 with an optional weight."""
95
95
 
96
96
  if key_vertex1 not in self.vertices:
97
97
  self.add_vertex(key_vertex1)
@@ -124,7 +124,7 @@ class Graph:
124
124
 
125
125
  return visited
126
126
 
127
- def depth_first_search(self, node_id, visited: Optional[List] = None):
127
+ def depth_first_search(self, node_id, visited: list | None = None):
128
128
  """
129
129
  Depth First Traversal Algorithm...
130
130
 
@@ -147,7 +147,7 @@ class Graph:
147
147
 
148
148
  return visited
149
149
 
150
- def find_path(self, start, end, path: Optional[List] = None):
150
+ def find_path(self, start, end, path: list | None = None):
151
151
  """
152
152
  Determine a path between two nodes...
153
153
 
@@ -178,7 +178,7 @@ class Graph:
178
178
 
179
179
  return None
180
180
 
181
- def find_all_paths(self, start, end, path: Optional[List] = None):
181
+ def find_all_paths(self, start, end, path: list | None = None):
182
182
  """
183
183
  Determine all paths between two nodes...
184
184
 
@@ -1,11 +1,8 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Vertex node used within graph data structures."""
4
2
 
5
3
  import sys
6
- from typing import Dict, List
7
4
 
8
- if sys.version_info >= (3, 11):
5
+ if sys.version_info >= (3, 11): # pragma: no cover
9
6
  from typing import Self
10
7
  else: # pragma: no cover
11
8
  from typing_extensions import Self
@@ -15,7 +12,7 @@ class Vertex:
15
12
  """Represents a node in a graph with weighted connections to neighbors."""
16
13
 
17
14
  def __init__(self, key):
18
- """ Initialize the vertex with a key and no connections. """
15
+ """Initialize the vertex with a key and no connections."""
19
16
 
20
17
  self._id = key
21
18
  self._connections = {}
@@ -25,7 +22,7 @@ class Vertex:
25
22
 
26
23
  @property
27
24
  def id(self):
28
- """ The vertex identifier. """
25
+ """The vertex identifier."""
29
26
  return self._id
30
27
 
31
28
  @id.setter
@@ -33,17 +30,17 @@ class Vertex:
33
30
  self._id = value
34
31
 
35
32
  def get_weight(self, vertex: Self):
36
- """ Retrieve the weight to reach the neighbor """
33
+ """Retrieve the weight to reach the neighbor"""
37
34
  return self._connections[vertex]
38
35
 
39
- def get_connections(self) -> Dict:
40
- """ Return the connections """
36
+ def get_connections(self) -> dict:
37
+ """Return the connections"""
41
38
  return self._connections
42
39
 
43
- def get_connections_ids(self) -> List:
44
- """ Return the connections ids """
40
+ def get_connections_ids(self) -> list:
41
+ """Return the connections ids"""
45
42
  return [vertex.id for vertex in self.get_connections()]
46
43
 
47
44
  def add_neighbor(self, vertex: Self, weight=0):
48
- """ Add or update a connection """
45
+ """Add or update a connection"""
49
46
  self._connections[vertex] = weight
@@ -1,11 +1,11 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Abstract interface shared by every list implementation in this package."""
4
2
 
5
- from abc import ABC
6
- from abc import abstractmethod
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from collections.abc import Iterator
7
7
  from contextlib import suppress
8
- from typing import Generic, Iterator, Optional, TypeVar, cast
8
+ from typing import Generic, TypeVar, cast
9
9
 
10
10
  from core_mixins.compatibility import Self
11
11
 
@@ -18,26 +18,26 @@ class Node(BaseNode[T]):
18
18
 
19
19
  def __init__(
20
20
  self,
21
- value: Optional[T] = None,
22
- next_node: Optional[Self] = None,
21
+ value: T | None = None,
22
+ next_node: Self | None = None,
23
23
  ) -> None:
24
24
  super().__init__(value=value)
25
25
  self._next = next_node
26
26
 
27
27
  @property
28
- def next(self) -> Optional[Self]:
28
+ def next(self) -> Self | None:
29
29
  """The next node in the list, if any."""
30
30
  return self._next
31
31
 
32
32
  @next.setter
33
- def next(self, next_node: Optional[Self]) -> None:
33
+ def next(self, next_node: Self | None) -> None:
34
34
  self._next = next_node
35
35
 
36
36
 
37
37
  NodeT = TypeVar("NodeT", bound=Node)
38
38
 
39
39
 
40
- class IList(Generic[T], ABC):
40
+ class IList(ABC, Generic[T]):
41
41
  """Abstract base class (interface) for all list implementations."""
42
42
 
43
43
  def __init__(self, length: int = 0):
@@ -177,13 +177,11 @@ class IList(Generic[T], ABC):
177
177
  """Reverses the list in place."""
178
178
 
179
179
 
180
- class ILinkedList(IList[T], Generic[T, NodeT], ABC):
180
+ class ILinkedList(IList[T], ABC, Generic[T, NodeT]):
181
181
  """Interface for linked list implementations, parametrized by their node type."""
182
182
 
183
- def __init__(self, value: Optional[T] = None) -> None:
184
- self._head: Optional[NodeT] = (
185
- None if value is None else self._create_node(value)
186
- )
183
+ def __init__(self, value: T | None = None) -> None:
184
+ self._head: NodeT | None = None if value is None else self._create_node(value)
187
185
 
188
186
  super().__init__(0 if value is None else 1)
189
187
  self._end = self._head
@@ -202,12 +200,12 @@ class ILinkedList(IList[T], Generic[T, NodeT], ABC):
202
200
  """Creates a new, unlinked node of the concrete node type used by this list."""
203
201
 
204
202
  @property
205
- def head(self) -> Optional[NodeT]:
203
+ def head(self) -> NodeT | None:
206
204
  """The first node in the list."""
207
205
  return self._head
208
206
 
209
207
  @property
210
- def end(self) -> Optional[NodeT]:
208
+ def end(self) -> NodeT | None:
211
209
  """The end node in the list."""
212
210
  return self._end
213
211
 
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """
4
2
  A doubly linked list is a linear data structure composed of a sequence
5
3
  of nodes where each node contains data and pointers (or references) to
@@ -12,7 +10,8 @@ direction.
12
10
  # ILinkedList rather than parent/child, so their mutators intentionally
13
11
  # don't share an implementation, even where the resulting code overlaps.
14
12
 
15
- from typing import Optional
13
+ from __future__ import annotations
14
+
16
15
  from typing import cast
17
16
 
18
17
  from core_mixins.compatibility import Self
@@ -30,20 +29,20 @@ class Node(LinkedNode[T]):
30
29
 
31
30
  def __init__(
32
31
  self,
33
- value: Optional[T] = None,
34
- next_node: Optional[Self] = None,
35
- prev_node: Optional[Self] = None,
32
+ value: T | None = None,
33
+ next_node: Self | None = None,
34
+ prev_node: Self | None = None,
36
35
  ) -> None:
37
36
  super().__init__(value=value, next_node=next_node)
38
37
  self._prev = prev_node
39
38
 
40
39
  @property
41
- def prev(self) -> Optional[Self]:
40
+ def prev(self) -> Self | None:
42
41
  """The previous node in the list, if any."""
43
42
  return self._prev
44
43
 
45
44
  @prev.setter
46
- def prev(self, prev_node: Optional[Self]) -> None:
45
+ def prev(self, prev_node: Self | None) -> None:
47
46
  self._prev = prev_node
48
47
 
49
48
 
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """
4
2
  A linked list is a linear data structure composed of a sequence
5
3
  of nodes where each node contains data and a pointer (or reference)
@@ -11,8 +9,7 @@ across your system's RAM.
11
9
  from typing import cast
12
10
 
13
11
  from data_structures.base import T
14
- from data_structures.lists.base import ILinkedList
15
- from data_structures.lists.base import Node
12
+ from data_structures.lists.base import ILinkedList, Node
16
13
 
17
14
 
18
15
  class SinglyLinkedList(ILinkedList[T, Node[T]]):
@@ -1,11 +1,8 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Tree data structures package."""
4
2
 
5
3
  from .binary_tree import BinaryTree
6
4
  from .simple_tree import Tree
7
5
 
8
-
9
6
  __all__ = [
10
7
  "BinaryTree",
11
8
  "Tree",
@@ -0,0 +1,44 @@
1
+ """Node with left/right child references, shared by every binary tree."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from core_mixins.compatibility import Self
6
+
7
+ from data_structures.base import Node as BaseNode
8
+ from data_structures.base import T
9
+
10
+ __all__ = [
11
+ "Node",
12
+ ]
13
+
14
+
15
+ class Node(BaseNode[T]):
16
+ """Node with references to its left and right children."""
17
+
18
+ def __init__(
19
+ self,
20
+ value: T | None = None,
21
+ left: Self | None = None,
22
+ right: Self | None = None,
23
+ ) -> None:
24
+ super().__init__(value=value)
25
+ self._left = left
26
+ self._right = right
27
+
28
+ @property
29
+ def left(self) -> Self | None:
30
+ """The left child of the node, if any."""
31
+ return self._left
32
+
33
+ @left.setter
34
+ def left(self, node: Self | None) -> None:
35
+ self._left = node
36
+
37
+ @property
38
+ def right(self) -> Self | None:
39
+ """The right child of the node, if any."""
40
+ return self._right
41
+
42
+ @right.setter
43
+ def right(self, node: Self | None) -> None:
44
+ self._right = node
@@ -0,0 +1,247 @@
1
+ """Binary tree data structure with recursive and iterative traversal algorithms."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from queue import Queue
7
+ from typing import Generic, cast
8
+
9
+ from data_structures.base import T
10
+ from data_structures.trees.base import Node
11
+
12
+
13
+ class BinaryTree(Generic[T]):
14
+ """
15
+ In computer science, a binary tree is a tree data structure in which
16
+ each node has at most two children, referred to as the left child
17
+ and the right child.
18
+ """
19
+
20
+ def __init__(self, value: T | None = None) -> None:
21
+ self._root: Node[T] | None = None if value is None else Node(value)
22
+
23
+ def __iter__(self) -> Iterator[T]:
24
+ yield from self._iter(self._root)
25
+
26
+ @classmethod
27
+ def _iter(cls, node: Node[T] | None) -> Iterator[T]:
28
+ if node is None:
29
+ return
30
+
31
+ yield from cls._iter(node.left)
32
+ yield cast(T, node.value)
33
+ yield from cls._iter(node.right)
34
+
35
+ def __len__(self) -> int:
36
+ return self._count(self._root)
37
+
38
+ @classmethod
39
+ def _count(cls, node: Node[T] | None) -> int:
40
+ if node is None:
41
+ return 0
42
+
43
+ return 1 + cls._count(node.left) + cls._count(node.right)
44
+
45
+ def __contains__(self, value: T) -> bool:
46
+ return any(node_value == value for node_value in self)
47
+
48
+ def __repr__(self) -> str:
49
+ return f"{type(self).__name__}({self.level_order()!r})"
50
+
51
+ @property
52
+ def root(self) -> Node[T] | None:
53
+ """The root node of the tree."""
54
+ return self._root
55
+
56
+ def pre_order(self) -> list[T]:
57
+ """
58
+ >>> tree = create_example_tree()
59
+ >>> tree.pre_order()
60
+ [1, 2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15]
61
+ """
62
+
63
+ return self._pre_order(self._root)
64
+
65
+ @classmethod
66
+ def _pre_order(cls, node: Node[T] | None) -> list[T]:
67
+ if node is None:
68
+ return []
69
+
70
+ res = [cast(T, node.value)]
71
+ res.extend(cls._pre_order(node.left))
72
+ res.extend(cls._pre_order(node.right))
73
+ return res
74
+
75
+ def pre_order_iterative(self) -> list[T]:
76
+ """
77
+ >>> tree = create_example_tree()
78
+ >>> tree.pre_order_iterative()
79
+ [1, 2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15]
80
+ """
81
+
82
+ stack: list[Node[T]] = []
83
+ node: Node[T] | None = self._root
84
+ res: list[T] = []
85
+
86
+ while stack or node:
87
+ while node:
88
+ res.append(cast(T, node.value))
89
+ stack.append(node)
90
+ node = node.left
91
+
92
+ node = stack.pop()
93
+ node = node.right
94
+
95
+ return res
96
+
97
+ def in_order(self) -> list[T]:
98
+ """
99
+ >>> tree = create_example_tree()
100
+ >>> tree.in_order()
101
+ [8, 4, 9, 2, 10, 5, 11, 1, 12, 6, 13, 3, 14, 7, 15]
102
+ """
103
+
104
+ return self._in_order(self._root)
105
+
106
+ @classmethod
107
+ def _in_order(cls, node: Node[T] | None) -> list[T]:
108
+ if node is None:
109
+ return []
110
+
111
+ res = cls._in_order(node.left)
112
+ res.append(cast(T, node.value))
113
+ res.extend(cls._in_order(node.right))
114
+ return res
115
+
116
+ def in_order_iterative(self) -> list[T]:
117
+ """
118
+ >>> tree = create_example_tree()
119
+ >>> tree.in_order_iterative()
120
+ [8, 4, 9, 2, 10, 5, 11, 1, 12, 6, 13, 3, 14, 7, 15]
121
+ """
122
+
123
+ res: list[T] = []
124
+ stack: list[Node[T]] = []
125
+ node: Node[T] | None = self._root
126
+
127
+ while stack or node:
128
+ while node:
129
+ stack.append(node)
130
+ node = node.left
131
+
132
+ node = stack.pop()
133
+ res.append(cast(T, node.value))
134
+ node = node.right
135
+
136
+ return res
137
+
138
+ def post_order(self) -> list[T]:
139
+ """
140
+ >>> tree = create_example_tree()
141
+ >>> tree.post_order()
142
+ [8, 9, 4, 10, 11, 5, 2, 12, 13, 6, 14, 15, 7, 3, 1]
143
+ """
144
+
145
+ return self._post_order(self._root)
146
+
147
+ @classmethod
148
+ def _post_order(cls, node: Node[T] | None) -> list[T]:
149
+ if node is None:
150
+ return []
151
+
152
+ res = cls._post_order(node.left)
153
+ res.extend(cls._post_order(node.right))
154
+ res.append(cast(T, node.value))
155
+ return res
156
+
157
+ def post_order_iterative(self) -> list[T]:
158
+ """
159
+ >>> tree = create_example_tree()
160
+ >>> tree.post_order_iterative()
161
+ [8, 9, 4, 10, 11, 5, 2, 12, 13, 6, 14, 15, 7, 3, 1]
162
+ """
163
+
164
+ if self._root is None:
165
+ return []
166
+
167
+ stack: list[Node[T]] = [self._root]
168
+ stack_tmp: list[Node[T]] = []
169
+
170
+ while stack:
171
+ node = stack.pop()
172
+ if node.left:
173
+ stack.append(node.left)
174
+
175
+ if node.right:
176
+ stack.append(node.right)
177
+
178
+ stack_tmp.append(node)
179
+
180
+ res: list[T] = []
181
+ while stack_tmp:
182
+ res.append(cast(T, stack_tmp.pop().value))
183
+
184
+ return res
185
+
186
+ def level_order(self) -> list[T]:
187
+ """
188
+ >>> tree = create_example_tree()
189
+ >>> tree.level_order()
190
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
191
+ """
192
+
193
+ res: list[T] = []
194
+ if self._root is None:
195
+ return res
196
+
197
+ q: Queue[Node[T]] = Queue()
198
+ q.put(self._root)
199
+
200
+ while not q.empty():
201
+ node_dequeued = q.get()
202
+ res.append(cast(T, node_dequeued.value))
203
+
204
+ if node_dequeued.left:
205
+ q.put(node_dequeued.left)
206
+
207
+ if node_dequeued.right:
208
+ q.put(node_dequeued.right)
209
+
210
+ return res
211
+
212
+ def depth(self) -> int:
213
+ """
214
+ The maximum depth of a binary tree is the number of nodes from the
215
+ root down to the furthest leaf node. In other words, it is
216
+ the height of a binary tree.
217
+
218
+ >>> tree = create_example_tree()
219
+ >>> tree.depth()
220
+ 4
221
+ """
222
+
223
+ return self._depth(self._root)
224
+
225
+ @classmethod
226
+ def _depth(cls, node: Node[T] | None) -> int:
227
+ if node is None:
228
+ return 0
229
+
230
+ return 1 + max(cls._depth(node.left), cls._depth(node.right))
231
+
232
+
233
+ def create_example_tree() -> BinaryTree[int]:
234
+ """Build and return a complete binary tree with 15 nodes for testing."""
235
+
236
+ tree = BinaryTree(1)
237
+ root = cast(Node[int], tree.root)
238
+ nodes = [Node(value) for value in range(2, 16)]
239
+
240
+ root.left, root.right = nodes[0], nodes[1]
241
+ nodes[0].left, nodes[0].right = nodes[2], nodes[3]
242
+ nodes[1].left, nodes[1].right = nodes[4], nodes[5]
243
+ nodes[2].left, nodes[2].right = nodes[6], nodes[7]
244
+ nodes[3].left, nodes[3].right = nodes[8], nodes[9]
245
+ nodes[4].left, nodes[4].right = nodes[10], nodes[11]
246
+ nodes[5].left, nodes[5].right = nodes[12], nodes[13]
247
+ return tree
@@ -1,12 +1,8 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Simple tree data structure backed by a nested dictionary."""
4
2
 
5
- from typing import Dict
6
-
7
3
 
8
- class Tree(Dict):
9
- """ Basic implementation based on Dictionary """
4
+ class Tree(dict):
5
+ """Basic implementation based on Dictionary"""
10
6
 
11
7
  def __missing__(self, key):
12
8
  value = self[key] = type(self)()
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
  [project]
10
10
  name = "core-data-structures"
11
11
  description = "This project/library contains commons data structures..."
12
- version = "1.3.0"
12
+ version = "1.4.1"
13
13
 
14
14
  authors = [
15
15
  {name = "Alejandro Cora González", email = "alek.cora.glez@gmail.com"}
@@ -37,6 +37,7 @@ classifiers = [
37
37
  "Programming Language :: Python :: 3.13",
38
38
  "Programming Language :: Python :: 3.14",
39
39
  "Programming Language :: Python :: Implementation :: PyPy",
40
+ "Typing :: Typed",
40
41
  ]
41
42
 
42
43
  dependencies = [
@@ -47,7 +48,7 @@ dependencies = [
47
48
  [project.optional-dependencies]
48
49
  dev = [
49
50
  "core-dev-tools>=2.1.0",
50
- "core-tests>=2.1.0",
51
+ "core-tests>=2.3.0",
51
52
  ]
52
53
 
53
54
  [project.urls]
@@ -0,0 +1,5 @@
1
+ """Setuptools entry point for building and installing the package."""
2
+
3
+ from setuptools import setup
4
+
5
+ setup()
@@ -1,230 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- """Binary tree data structure with recursive and iterative traversal algorithms."""
4
-
5
- from queue import Queue
6
- from typing import List, Optional
7
-
8
-
9
- class BinaryTree:
10
- """
11
- In computer science, a binary tree is a tree data structure in which
12
- each node has at most two children, which are referred to as the
13
- left child and the right child...
14
- """
15
-
16
- def __init__(self, value) -> None:
17
- self.value = value
18
- self.right: Optional[BinaryTree] = None
19
- self.left: Optional[BinaryTree] = None
20
-
21
- @staticmethod
22
- def create_example_tree():
23
- """ Build and return a complete binary tree with 15 nodes for testing. """
24
-
25
- root = BinaryTree(1)
26
- tree_node2, tree_node3 = BinaryTree(2), BinaryTree(3)
27
- tree_node4, tree_node5 = BinaryTree(4), BinaryTree(5)
28
- tree_node6, tree_node7 = BinaryTree(6), BinaryTree(7)
29
- tree_node8, tree_node9 = BinaryTree(8), BinaryTree(9)
30
- tree_node10, tree_node11 = BinaryTree(10), BinaryTree(11)
31
- tree_node12, tree_node13 = BinaryTree(12), BinaryTree(13)
32
- tree_node14, tree_node15 = BinaryTree(14), BinaryTree(15)
33
-
34
- root.left, root.right = tree_node2, tree_node3
35
- tree_node2.left, tree_node2.right = tree_node4, tree_node5
36
- tree_node3.left, tree_node3.right = tree_node6, tree_node7
37
- tree_node4.left, tree_node4.right = tree_node8, tree_node9
38
- tree_node5.left, tree_node5.right = tree_node10, tree_node11
39
- tree_node6.left, tree_node6.right = tree_node12, tree_node13
40
- tree_node7.left, tree_node7.right = tree_node14, tree_node15
41
- return root
42
-
43
- def pre_order(self) -> List:
44
- """
45
- >>> root = BinaryTree.create_example_tree()
46
- >>> root.pre_order()
47
- [1, 2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15]
48
- """
49
-
50
- res = [self.value]
51
- if self.left:
52
- res.extend(self.left.pre_order())
53
-
54
- if self.right:
55
- res.extend(self.right.pre_order())
56
-
57
- return res
58
-
59
- def pre_order_iterative(self) -> List:
60
- """
61
- >>> root = BinaryTree.create_example_tree()
62
- >>> root.pre_order_iterative()
63
- [1, 2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15]
64
- """
65
-
66
- stack: List[BinaryTree] = []
67
- node: Optional[BinaryTree] = self
68
- res = []
69
-
70
- while stack or node:
71
- while node:
72
- res.append(node.value)
73
- stack.append(node)
74
- node = node.left
75
-
76
- node = stack.pop()
77
- node = node.right
78
-
79
- return res
80
-
81
- def in_order(self) -> List:
82
- """
83
- >>> root = BinaryTree.create_example_tree()
84
- >>> root.in_order()
85
- [8, 4, 9, 2, 10, 5, 11, 1, 12, 6, 13, 3, 14, 7, 15]
86
- """
87
-
88
- res = []
89
- if self.left:
90
- res.extend(self.left.in_order())
91
-
92
- res.append(self.value)
93
-
94
- if self.right:
95
- res.extend(self.right.in_order())
96
-
97
- return res
98
-
99
- def in_order_iterative(self) -> List:
100
- """
101
- >>> root = BinaryTree.create_example_tree()
102
- >>> root.in_order_iterative()
103
- [8, 4, 9, 2, 10, 5, 11, 1, 12, 6, 13, 3, 14, 7, 15]
104
- """
105
-
106
- res = []
107
- stack: List[BinaryTree] = []
108
- node: Optional[BinaryTree] = self
109
-
110
- while stack or node:
111
- while node:
112
- stack.append(node)
113
- node = node.left
114
-
115
- node = stack.pop()
116
- res.append(node.value)
117
- node = node.right
118
-
119
- return res
120
-
121
- def post_order(self) -> List:
122
- """
123
- >>> root = BinaryTree.create_example_tree()
124
- >>> root.post_order()
125
- [8, 9, 4, 10, 11, 5, 2, 12, 13, 6, 14, 15, 7, 3, 1]
126
- """
127
-
128
- res = []
129
- if self.left:
130
- res.extend(self.left.post_order())
131
-
132
- if self.right:
133
- res.extend(self.right.post_order())
134
-
135
- res.append(self.value)
136
- return res
137
-
138
- def post_order_iterative(self) -> List:
139
- """
140
- >>> root = BinaryTree.create_example_tree()
141
- >>> root.post_order_iterative()
142
- [8, 9, 4, 10, 11, 5, 2, 12, 13, 6, 14, 15, 7, 3, 1]
143
- """
144
-
145
- stack: List[BinaryTree] = [self]
146
- stack_tmp: List[BinaryTree] = []
147
-
148
- while stack:
149
- node = stack.pop()
150
- if node.left:
151
- stack.append(node.left)
152
-
153
- if node.right:
154
- stack.append(node.right)
155
-
156
- stack_tmp.append(node)
157
-
158
- res = []
159
- while stack_tmp:
160
- res.append(stack_tmp.pop().value)
161
-
162
- return res
163
-
164
- def level_order(self) -> List:
165
- """
166
- >>> root = BinaryTree.create_example_tree()
167
- >>> root.level_order()
168
- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
169
- """
170
-
171
- res = []
172
- q: Queue[BinaryTree] = Queue()
173
- q.put(self)
174
-
175
- while not q.empty():
176
- node_dequeued = q.get()
177
- res.append(node_dequeued.value)
178
-
179
- if node_dequeued.left:
180
- q.put(node_dequeued.left)
181
-
182
- if node_dequeued.right:
183
- q.put(node_dequeued.right)
184
-
185
- return res
186
-
187
- # def level_order_iterative(self) -> List:
188
- # """
189
- # >>> root = BinaryTree.create_example_tree()
190
- # >>> root.post_order_iterative()
191
- # [8, 9, 4, 10, 11, 5, 2, 12, 13, 6, 14, 15, 7, 3, 1]
192
- # """
193
- #
194
- # stack: List[BinaryTree] = []
195
- # node, res = self, []
196
- #
197
- # while stack or node:
198
- # while node.left:
199
- # stack.append(node)
200
- # node = node.left
201
- #
202
- # res.append(node.value)
203
- # node = stack.pop()
204
- # node = node.right
205
- #
206
- # return res
207
-
208
- def depth(self):
209
- """
210
- The maximum depth of a binary tree is the number of nodes from the
211
- root down to the furthest leaf node. In other words, it is
212
- the height of a binary tree.
213
-
214
- >>> root = BinaryTree.create_example_tree()
215
- >>> root.depth()
216
- 4
217
- """
218
-
219
- if not self.left and not self.right:
220
- return 1
221
-
222
- depth_left = 0
223
- if self.left:
224
- depth_left = self.left.depth()
225
-
226
- depth_right = 0
227
- if self.right:
228
- depth_right = self.right.depth()
229
-
230
- return 1 + max(depth_left, depth_right)
@@ -1,6 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- from setuptools import setup
4
-
5
-
6
- setup()