core-data-structures 1.2.1__tar.gz → 1.4.0__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.
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/PKG-INFO +12 -3
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/README.rst +11 -2
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/core_data_structures.egg-info/PKG-INFO +12 -3
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/core_data_structures.egg-info/SOURCES.txt +3 -0
- core_data_structures-1.4.0/data_structures/lists/base.py +233 -0
- core_data_structures-1.4.0/data_structures/lists/double_linked_list.py +231 -0
- core_data_structures-1.4.0/data_structures/lists/singly_linked_list.py +180 -0
- core_data_structures-1.4.0/data_structures/trees/base.py +46 -0
- core_data_structures-1.4.0/data_structures/trees/binary_tree.py +246 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/pyproject.toml +1 -1
- core_data_structures-1.2.1/data_structures/lists/singly_linked_list.py +0 -308
- core_data_structures-1.2.1/data_structures/trees/binary_tree.py +0 -230
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/LICENSE +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/core_data_structures.egg-info/dependency_links.txt +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/core_data_structures.egg-info/requires.txt +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/core_data_structures.egg-info/top_level.txt +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/data_structures/__init__.py +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/data_structures/base.py +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/data_structures/graphs/__init__.py +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/data_structures/graphs/graphs.py +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/data_structures/graphs/vertex.py +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/data_structures/lists/__init__.py +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/data_structures/py.typed +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/data_structures/trees/__init__.py +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/data_structures/trees/simple_tree.py +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/setup.cfg +0 -0
- {core_data_structures-1.2.1 → core_data_structures-1.4.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-data-structures
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
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
|
|
@@ -35,8 +35,8 @@ Dynamic: license-file
|
|
|
35
35
|
core-data-structures
|
|
36
36
|
===============================================================================
|
|
37
37
|
|
|
38
|
-
A Python library providing common data structures including graphs
|
|
39
|
-
with built-in traversal
|
|
38
|
+
A Python library providing common data structures including graphs, trees, and
|
|
39
|
+
lists, with built-in traversal, path-finding, and search algorithms.
|
|
40
40
|
|
|
41
41
|
===============================================================================
|
|
42
42
|
|
|
@@ -82,6 +82,15 @@ Features
|
|
|
82
82
|
- Recursive and iterative variants
|
|
83
83
|
- Tree depth calculation
|
|
84
84
|
|
|
85
|
+
- **Lists** — singly and doubly linked lists sharing a common ``IList``/``ILinkedList`` interface.
|
|
86
|
+
|
|
87
|
+
- Value-based CRUD: ``append``, ``prepend``, ``insert``, ``get``/``__getitem__``,
|
|
88
|
+
``index``, ``pop``/``pop_front``/``pop_back``, ``remove`` (single or all occurrences)
|
|
89
|
+
- ``reverse``, ``extend``, ``clear``, ``to_list``/``from_list``
|
|
90
|
+
- ``__iter__``/``__contains__`` support, plus O(1) ``head``/``end`` node access
|
|
91
|
+
- ``DoubleLinkedList`` additionally maintains ``.prev`` links for backward traversal
|
|
92
|
+
and locates positions from whichever end (``head``/``end``) is closer
|
|
93
|
+
|
|
85
94
|
|
|
86
95
|
Quick Start
|
|
87
96
|
===============================================================================
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
core-data-structures
|
|
2
2
|
===============================================================================
|
|
3
3
|
|
|
4
|
-
A Python library providing common data structures including graphs
|
|
5
|
-
with built-in traversal
|
|
4
|
+
A Python library providing common data structures including graphs, trees, and
|
|
5
|
+
lists, with built-in traversal, path-finding, and search algorithms.
|
|
6
6
|
|
|
7
7
|
===============================================================================
|
|
8
8
|
|
|
@@ -48,6 +48,15 @@ Features
|
|
|
48
48
|
- Recursive and iterative variants
|
|
49
49
|
- Tree depth calculation
|
|
50
50
|
|
|
51
|
+
- **Lists** — singly and doubly linked lists sharing a common ``IList``/``ILinkedList`` interface.
|
|
52
|
+
|
|
53
|
+
- Value-based CRUD: ``append``, ``prepend``, ``insert``, ``get``/``__getitem__``,
|
|
54
|
+
``index``, ``pop``/``pop_front``/``pop_back``, ``remove`` (single or all occurrences)
|
|
55
|
+
- ``reverse``, ``extend``, ``clear``, ``to_list``/``from_list``
|
|
56
|
+
- ``__iter__``/``__contains__`` support, plus O(1) ``head``/``end`` node access
|
|
57
|
+
- ``DoubleLinkedList`` additionally maintains ``.prev`` links for backward traversal
|
|
58
|
+
and locates positions from whichever end (``head``/``end``) is closer
|
|
59
|
+
|
|
51
60
|
|
|
52
61
|
Quick Start
|
|
53
62
|
===============================================================================
|
{core_data_structures-1.2.1 → core_data_structures-1.4.0}/core_data_structures.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-data-structures
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
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
|
|
@@ -35,8 +35,8 @@ Dynamic: license-file
|
|
|
35
35
|
core-data-structures
|
|
36
36
|
===============================================================================
|
|
37
37
|
|
|
38
|
-
A Python library providing common data structures including graphs
|
|
39
|
-
with built-in traversal
|
|
38
|
+
A Python library providing common data structures including graphs, trees, and
|
|
39
|
+
lists, with built-in traversal, path-finding, and search algorithms.
|
|
40
40
|
|
|
41
41
|
===============================================================================
|
|
42
42
|
|
|
@@ -82,6 +82,15 @@ Features
|
|
|
82
82
|
- Recursive and iterative variants
|
|
83
83
|
- Tree depth calculation
|
|
84
84
|
|
|
85
|
+
- **Lists** — singly and doubly linked lists sharing a common ``IList``/``ILinkedList`` interface.
|
|
86
|
+
|
|
87
|
+
- Value-based CRUD: ``append``, ``prepend``, ``insert``, ``get``/``__getitem__``,
|
|
88
|
+
``index``, ``pop``/``pop_front``/``pop_back``, ``remove`` (single or all occurrences)
|
|
89
|
+
- ``reverse``, ``extend``, ``clear``, ``to_list``/``from_list``
|
|
90
|
+
- ``__iter__``/``__contains__`` support, plus O(1) ``head``/``end`` node access
|
|
91
|
+
- ``DoubleLinkedList`` additionally maintains ``.prev`` links for backward traversal
|
|
92
|
+
and locates positions from whichever end (``head``/``end``) is closer
|
|
93
|
+
|
|
85
94
|
|
|
86
95
|
Quick Start
|
|
87
96
|
===============================================================================
|
{core_data_structures-1.2.1 → core_data_structures-1.4.0}/core_data_structures.egg-info/SOURCES.txt
RENAMED
|
@@ -14,7 +14,10 @@ data_structures/graphs/__init__.py
|
|
|
14
14
|
data_structures/graphs/graphs.py
|
|
15
15
|
data_structures/graphs/vertex.py
|
|
16
16
|
data_structures/lists/__init__.py
|
|
17
|
+
data_structures/lists/base.py
|
|
18
|
+
data_structures/lists/double_linked_list.py
|
|
17
19
|
data_structures/lists/singly_linked_list.py
|
|
18
20
|
data_structures/trees/__init__.py
|
|
21
|
+
data_structures/trees/base.py
|
|
19
22
|
data_structures/trees/binary_tree.py
|
|
20
23
|
data_structures/trees/simple_tree.py
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""Abstract interface shared by every list implementation in this package."""
|
|
4
|
+
|
|
5
|
+
from abc import ABC
|
|
6
|
+
from abc import abstractmethod
|
|
7
|
+
from contextlib import suppress
|
|
8
|
+
from typing import Generic, Iterator, Optional, TypeVar, cast
|
|
9
|
+
|
|
10
|
+
from core_mixins.compatibility import Self
|
|
11
|
+
|
|
12
|
+
from data_structures.base import Node as BaseNode
|
|
13
|
+
from data_structures.base import T
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Node(BaseNode[T]):
|
|
17
|
+
"""Node with a reference to the next node, shared by every linked list."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
value: Optional[T] = None,
|
|
22
|
+
next_node: Optional[Self] = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
super().__init__(value=value)
|
|
25
|
+
self._next = next_node
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def next(self) -> Optional[Self]:
|
|
29
|
+
"""The next node in the list, if any."""
|
|
30
|
+
return self._next
|
|
31
|
+
|
|
32
|
+
@next.setter
|
|
33
|
+
def next(self, next_node: Optional[Self]) -> None:
|
|
34
|
+
self._next = next_node
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
NodeT = TypeVar("NodeT", bound=Node)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class IList(Generic[T], ABC):
|
|
41
|
+
"""Abstract base class (interface) for all list implementations."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, length: int = 0):
|
|
44
|
+
self._length = length
|
|
45
|
+
|
|
46
|
+
def __len__(self) -> int:
|
|
47
|
+
"""Returns the length of the list."""
|
|
48
|
+
return self._length
|
|
49
|
+
|
|
50
|
+
def __getitem__(self, item: int) -> T:
|
|
51
|
+
"""Returns the value at the given position."""
|
|
52
|
+
return self.get(item)
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def __iter__(self) -> Iterator[T]:
|
|
56
|
+
"""Iterates over the values in the list, starting from the head."""
|
|
57
|
+
|
|
58
|
+
def __contains__(self, item: T) -> bool:
|
|
59
|
+
"""Returns whether the value is present in the list."""
|
|
60
|
+
|
|
61
|
+
with suppress(ValueError):
|
|
62
|
+
self.index(item)
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def length(self) -> int:
|
|
69
|
+
"""The number of elements in the list."""
|
|
70
|
+
return self._length
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def is_empty(self) -> bool:
|
|
74
|
+
"""Whether the list is empty."""
|
|
75
|
+
return self._length == 0
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_list(cls, a_list: list) -> Self:
|
|
79
|
+
"""Creates the list from a python list."""
|
|
80
|
+
|
|
81
|
+
new_list = cls()
|
|
82
|
+
for value in a_list:
|
|
83
|
+
new_list.append(value)
|
|
84
|
+
|
|
85
|
+
return new_list
|
|
86
|
+
|
|
87
|
+
def to_list(self) -> list:
|
|
88
|
+
"""Materializes the list's values into a python list."""
|
|
89
|
+
return list(self)
|
|
90
|
+
|
|
91
|
+
def index(self, value: T) -> int:
|
|
92
|
+
"""Returns the position of the first occurrence of the given value."""
|
|
93
|
+
|
|
94
|
+
for pos, value_ in enumerate(self):
|
|
95
|
+
if value_ == value:
|
|
96
|
+
return pos
|
|
97
|
+
|
|
98
|
+
raise ValueError(f"{value!r} is not in list")
|
|
99
|
+
|
|
100
|
+
@abstractmethod
|
|
101
|
+
def get(self, pos: int) -> T:
|
|
102
|
+
"""Returns the value at the given position."""
|
|
103
|
+
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def clear(self) -> None:
|
|
106
|
+
"""Removes all nodes from the list."""
|
|
107
|
+
|
|
108
|
+
@abstractmethod
|
|
109
|
+
def prepend(self, value: T) -> None:
|
|
110
|
+
"""Adds a value to the front of the list."""
|
|
111
|
+
|
|
112
|
+
@abstractmethod
|
|
113
|
+
def append(self, value: T) -> None:
|
|
114
|
+
"""Adds a value to the end of the list."""
|
|
115
|
+
|
|
116
|
+
@abstractmethod
|
|
117
|
+
def insert(self, value: T, pos: int) -> None:
|
|
118
|
+
"""Inserts a value at the given position."""
|
|
119
|
+
|
|
120
|
+
def extend(self, a_list: Self) -> None:
|
|
121
|
+
"""Appends a copy of each value from another list."""
|
|
122
|
+
|
|
123
|
+
for value in list(a_list):
|
|
124
|
+
self.append(value)
|
|
125
|
+
|
|
126
|
+
def pop_front(self) -> T:
|
|
127
|
+
"""Removes and returns the value at the front of the list."""
|
|
128
|
+
|
|
129
|
+
if self.is_empty:
|
|
130
|
+
raise IndexError("Empty list!")
|
|
131
|
+
|
|
132
|
+
return self._pop_front()
|
|
133
|
+
|
|
134
|
+
@abstractmethod
|
|
135
|
+
def _pop_front(self) -> T:
|
|
136
|
+
"""Removes and returns the value at the front of a non-empty list."""
|
|
137
|
+
|
|
138
|
+
def pop_back(self) -> T:
|
|
139
|
+
"""Removes and returns the value at the end of the list."""
|
|
140
|
+
|
|
141
|
+
if self.is_empty:
|
|
142
|
+
raise IndexError("Empty list!")
|
|
143
|
+
|
|
144
|
+
return self._pop_back()
|
|
145
|
+
|
|
146
|
+
@abstractmethod
|
|
147
|
+
def _pop_back(self) -> T:
|
|
148
|
+
"""Removes and returns the value at the end of a non-empty list."""
|
|
149
|
+
|
|
150
|
+
def pop(self, index: int) -> T:
|
|
151
|
+
"""Removes and returns the value at the given position."""
|
|
152
|
+
|
|
153
|
+
if index < 0 or index >= self._length:
|
|
154
|
+
raise IndexError("Index error!")
|
|
155
|
+
|
|
156
|
+
if index == 0:
|
|
157
|
+
return self.pop_front()
|
|
158
|
+
|
|
159
|
+
if index == self._length - 1:
|
|
160
|
+
return self.pop_back()
|
|
161
|
+
|
|
162
|
+
return self._pop_middle(index)
|
|
163
|
+
|
|
164
|
+
@abstractmethod
|
|
165
|
+
def _pop_middle(self, index: int) -> T:
|
|
166
|
+
"""Removes and returns the value at an interior position (not the first or last)."""
|
|
167
|
+
|
|
168
|
+
@abstractmethod
|
|
169
|
+
def remove(self, value: T, all_values: bool = False) -> None:
|
|
170
|
+
"""
|
|
171
|
+
Removes the first occurrence of the value, or all occurrences
|
|
172
|
+
if `all_values` is set.
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
@abstractmethod
|
|
176
|
+
def reverse(self) -> None:
|
|
177
|
+
"""Reverses the list in place."""
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class ILinkedList(IList[T], Generic[T, NodeT], ABC):
|
|
181
|
+
"""Interface for linked list implementations, parametrized by their node type."""
|
|
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
|
+
)
|
|
187
|
+
|
|
188
|
+
super().__init__(0 if value is None else 1)
|
|
189
|
+
self._end = self._head
|
|
190
|
+
|
|
191
|
+
def __iter__(self) -> Iterator[T]:
|
|
192
|
+
"""Iterates over the values in the list, starting from the head."""
|
|
193
|
+
|
|
194
|
+
pointer = self._head
|
|
195
|
+
while pointer:
|
|
196
|
+
yield cast(T, pointer.value)
|
|
197
|
+
pointer = pointer.next
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
@abstractmethod
|
|
201
|
+
def _create_node(cls, value: T) -> NodeT:
|
|
202
|
+
"""Creates a new, unlinked node of the concrete node type used by this list."""
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def head(self) -> Optional[NodeT]:
|
|
206
|
+
"""The first node in the list."""
|
|
207
|
+
return self._head
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def end(self) -> Optional[NodeT]:
|
|
211
|
+
"""The end node in the list."""
|
|
212
|
+
return self._end
|
|
213
|
+
|
|
214
|
+
def get(self, pos: int) -> T:
|
|
215
|
+
"""Returns the value at the given position."""
|
|
216
|
+
return cast(T, self._node_at(pos).value)
|
|
217
|
+
|
|
218
|
+
def _node_at(self, pos: int) -> NodeT:
|
|
219
|
+
"""Returns the actual node at the given position."""
|
|
220
|
+
|
|
221
|
+
if pos < 0 or pos >= self._length:
|
|
222
|
+
raise IndexError("Invalid position!")
|
|
223
|
+
|
|
224
|
+
return self._locate(pos)
|
|
225
|
+
|
|
226
|
+
@abstractmethod
|
|
227
|
+
def _locate(self, pos: int) -> NodeT:
|
|
228
|
+
"""Returns the node at an already-validated position."""
|
|
229
|
+
|
|
230
|
+
def clear(self) -> None:
|
|
231
|
+
"""Removes all nodes from the list."""
|
|
232
|
+
self._head = self._end = None
|
|
233
|
+
self._length = 0
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
A doubly linked list is a linear data structure composed of a sequence
|
|
5
|
+
of nodes where each node contains data and pointers (or references) to
|
|
6
|
+
both the next and the previous node, allowing traversal in either
|
|
7
|
+
direction.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# pylint: disable=duplicate-code
|
|
11
|
+
# SinglyLinkedList and DoubleLinkedList are independent siblings under
|
|
12
|
+
# ILinkedList rather than parent/child, so their mutators intentionally
|
|
13
|
+
# don't share an implementation, even where the resulting code overlaps.
|
|
14
|
+
|
|
15
|
+
from typing import Optional
|
|
16
|
+
from typing import cast
|
|
17
|
+
|
|
18
|
+
from core_mixins.compatibility import Self
|
|
19
|
+
|
|
20
|
+
from data_structures.base import T
|
|
21
|
+
from data_structures.lists.base import ILinkedList
|
|
22
|
+
from data_structures.lists.base import Node as LinkedNode
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Node(LinkedNode[T]):
|
|
26
|
+
"""
|
|
27
|
+
Node in a double linked list, holding a value and the
|
|
28
|
+
references to the next and previous nodes.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
value: Optional[T] = None,
|
|
34
|
+
next_node: Optional[Self] = None,
|
|
35
|
+
prev_node: Optional[Self] = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
super().__init__(value=value, next_node=next_node)
|
|
38
|
+
self._prev = prev_node
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def prev(self) -> Optional[Self]:
|
|
42
|
+
"""The previous node in the list, if any."""
|
|
43
|
+
return self._prev
|
|
44
|
+
|
|
45
|
+
@prev.setter
|
|
46
|
+
def prev(self, prev_node: Optional[Self]) -> None:
|
|
47
|
+
self._prev = prev_node
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DoubleLinkedList(ILinkedList[T, Node[T]]):
|
|
51
|
+
"""
|
|
52
|
+
Double linked list that tracks its head node and supports
|
|
53
|
+
forward and backward iteration. Positions are 0-indexed.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def _create_node(cls, value: T) -> Node[T]:
|
|
58
|
+
return Node(value)
|
|
59
|
+
|
|
60
|
+
def _locate(self, pos: int) -> Node[T]:
|
|
61
|
+
"""Returns the node at an already-validated position."""
|
|
62
|
+
|
|
63
|
+
if self._length - 1 - pos <= pos:
|
|
64
|
+
pointer = cast(Node[T], self._end)
|
|
65
|
+
index = self._length - 1
|
|
66
|
+
|
|
67
|
+
while index > pos:
|
|
68
|
+
pointer = cast(Node[T], pointer.prev)
|
|
69
|
+
index -= 1
|
|
70
|
+
|
|
71
|
+
else:
|
|
72
|
+
pointer = cast(Node[T], self._head)
|
|
73
|
+
index = 0
|
|
74
|
+
|
|
75
|
+
while index < pos:
|
|
76
|
+
pointer = cast(Node[T], pointer.next)
|
|
77
|
+
index += 1
|
|
78
|
+
|
|
79
|
+
return pointer
|
|
80
|
+
|
|
81
|
+
def prepend(self, value: T) -> None:
|
|
82
|
+
"""Adds a value to the front of the list."""
|
|
83
|
+
|
|
84
|
+
if self._head is None:
|
|
85
|
+
self.append(value)
|
|
86
|
+
|
|
87
|
+
else:
|
|
88
|
+
new_head = self._create_node(value)
|
|
89
|
+
new_head.next = self._head
|
|
90
|
+
self._head.prev = new_head
|
|
91
|
+
self._head = new_head
|
|
92
|
+
self._length += 1
|
|
93
|
+
|
|
94
|
+
def append(self, value: T) -> None:
|
|
95
|
+
"""Adds a value to the end of the list."""
|
|
96
|
+
|
|
97
|
+
new_node = self._create_node(value)
|
|
98
|
+
|
|
99
|
+
if self._end is None:
|
|
100
|
+
self._head = new_node
|
|
101
|
+
else:
|
|
102
|
+
self._end.next = new_node
|
|
103
|
+
new_node.prev = self._end
|
|
104
|
+
|
|
105
|
+
self._end = new_node
|
|
106
|
+
self._length += 1
|
|
107
|
+
|
|
108
|
+
def insert(self, value: T, pos: int) -> None:
|
|
109
|
+
"""Inserts a value at the given position."""
|
|
110
|
+
|
|
111
|
+
if pos == 0:
|
|
112
|
+
self.prepend(value)
|
|
113
|
+
|
|
114
|
+
elif pos == self._length:
|
|
115
|
+
self.append(value)
|
|
116
|
+
|
|
117
|
+
else:
|
|
118
|
+
pointer = self._node_at(pos - 1)
|
|
119
|
+
old_next = pointer.next
|
|
120
|
+
|
|
121
|
+
new_node = self._create_node(value)
|
|
122
|
+
new_node.next = old_next
|
|
123
|
+
new_node.prev = pointer
|
|
124
|
+
cast(Node[T], old_next).prev = new_node
|
|
125
|
+
|
|
126
|
+
pointer.next = new_node
|
|
127
|
+
self._length += 1
|
|
128
|
+
|
|
129
|
+
def _pop_front(self) -> T:
|
|
130
|
+
"""Removes and returns the value at the front of a non-empty list."""
|
|
131
|
+
|
|
132
|
+
head = cast(Node[T], self._head)
|
|
133
|
+
value = cast(T, head.value)
|
|
134
|
+
|
|
135
|
+
if self._length == 1:
|
|
136
|
+
self._head = None
|
|
137
|
+
self._end = None
|
|
138
|
+
else:
|
|
139
|
+
self._head = head.next
|
|
140
|
+
cast(Node[T], self._head).prev = None
|
|
141
|
+
|
|
142
|
+
self._length -= 1
|
|
143
|
+
return value
|
|
144
|
+
|
|
145
|
+
def _pop_back(self) -> T:
|
|
146
|
+
"""Removes and returns the value at the end of a non-empty list."""
|
|
147
|
+
|
|
148
|
+
end = cast(Node[T], self._end)
|
|
149
|
+
value = cast(T, end.value)
|
|
150
|
+
|
|
151
|
+
if self._length == 1:
|
|
152
|
+
self.clear()
|
|
153
|
+
return value
|
|
154
|
+
|
|
155
|
+
self._end = end.prev
|
|
156
|
+
cast(Node[T], self._end).next = None
|
|
157
|
+
self._length -= 1
|
|
158
|
+
return value
|
|
159
|
+
|
|
160
|
+
def _pop_middle(self, index: int) -> T:
|
|
161
|
+
"""Removes and returns the value at an interior position (not the first or last)."""
|
|
162
|
+
|
|
163
|
+
removed = self._node_at(index)
|
|
164
|
+
prev_node = cast(Node[T], removed.prev)
|
|
165
|
+
next_node = cast(Node[T], removed.next)
|
|
166
|
+
|
|
167
|
+
prev_node.next = next_node
|
|
168
|
+
next_node.prev = prev_node
|
|
169
|
+
|
|
170
|
+
self._length -= 1
|
|
171
|
+
return cast(T, removed.value)
|
|
172
|
+
|
|
173
|
+
def remove(self, value: T, all_values: bool = False) -> None:
|
|
174
|
+
"""
|
|
175
|
+
Removes the first occurrence of the value, or all occurrences
|
|
176
|
+
if `all_values` is set.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
was_found = False
|
|
180
|
+
|
|
181
|
+
while self._head is not None and self._head.value == value:
|
|
182
|
+
self._head = self._head.next
|
|
183
|
+
self._length -= 1
|
|
184
|
+
was_found = True
|
|
185
|
+
|
|
186
|
+
if self._head is None:
|
|
187
|
+
self._end = None
|
|
188
|
+
else:
|
|
189
|
+
self._head.prev = None
|
|
190
|
+
|
|
191
|
+
if not all_values:
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
pointer = self._head
|
|
195
|
+
while pointer is not None:
|
|
196
|
+
if pointer.value == value:
|
|
197
|
+
prev_node = cast(Node[T], pointer.prev)
|
|
198
|
+
next_node = pointer.next
|
|
199
|
+
|
|
200
|
+
prev_node.next = next_node
|
|
201
|
+
if next_node is not None:
|
|
202
|
+
next_node.prev = prev_node
|
|
203
|
+
|
|
204
|
+
if pointer is self._end:
|
|
205
|
+
self._end = prev_node
|
|
206
|
+
|
|
207
|
+
self._length -= 1
|
|
208
|
+
was_found = True
|
|
209
|
+
pointer = next_node
|
|
210
|
+
|
|
211
|
+
if not all_values:
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
else:
|
|
215
|
+
pointer = pointer.next
|
|
216
|
+
|
|
217
|
+
if not was_found:
|
|
218
|
+
raise ValueError(f"{value!r} is not in list")
|
|
219
|
+
|
|
220
|
+
def reverse(self) -> None:
|
|
221
|
+
"""Reverses the list in place."""
|
|
222
|
+
|
|
223
|
+
if self.is_empty or self._length == 1:
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
pointer = self._head
|
|
227
|
+
while pointer is not None:
|
|
228
|
+
pointer.next, pointer.prev = pointer.prev, pointer.next
|
|
229
|
+
pointer = pointer.prev
|
|
230
|
+
|
|
231
|
+
self._head, self._end = self._end, self._head
|