dsa-study 0.1.0__tar.gz → 0.4.2__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.
- dsa_study-0.4.2/.github/workflows/ci.yml +38 -0
- dsa_study-0.4.2/.github/workflows/publish.yml +26 -0
- dsa_study-0.4.2/.gitignore +21 -0
- dsa_study-0.4.2/.pre-commit-config.yaml +30 -0
- dsa_study-0.4.2/.python-version +1 -0
- {dsa_study-0.1.0/src/dsa_study.egg-info → dsa_study-0.4.2}/PKG-INFO +15 -11
- {dsa_study-0.1.0 → dsa_study-0.4.2}/README.md +11 -7
- dsa_study-0.4.2/justfile +15 -0
- dsa_study-0.4.2/pyproject.toml +44 -0
- dsa_study-0.4.2/src/dsa/data_structures/doubly_linked_list.py +320 -0
- dsa_study-0.4.2/src/dsa/data_structures/linked_list.py +200 -0
- dsa_study-0.4.2/tests/data_structures/test_doubly_linked_list.py +270 -0
- dsa_study-0.4.2/tests/data_structures/test_linked_list.py +172 -0
- dsa_study-0.4.2/uv.lock +401 -0
- dsa_study-0.1.0/PKG-INFO +0 -225
- dsa_study-0.1.0/pyproject.toml +0 -18
- dsa_study-0.1.0/setup.cfg +0 -4
- dsa_study-0.1.0/src/dsa/data_structures/linked_list.py +0 -107
- dsa_study-0.1.0/src/dsa_study.egg-info/SOURCES.txt +0 -11
- dsa_study-0.1.0/src/dsa_study.egg-info/dependency_links.txt +0 -1
- dsa_study-0.1.0/src/dsa_study.egg-info/top_level.txt +0 -1
- {dsa_study-0.1.0 → dsa_study-0.4.2}/LICENSE +0 -0
- {dsa_study-0.1.0 → dsa_study-0.4.2}/src/dsa/__init__.py +0 -0
- {dsa_study-0.1.0 → dsa_study-0.4.2}/src/dsa/algorithms/__init__.py +0 -0
- {dsa_study-0.1.0 → dsa_study-0.4.2}/src/dsa/data_structures/__init__.py +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# .github/workflows/ci.yml
|
|
2
|
+
|
|
3
|
+
name: CI
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
pull_request:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout repository
|
|
17
|
+
uses: actions/checkout@v5
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v6
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
run: uv python install
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync --all-extras --dev
|
|
27
|
+
|
|
28
|
+
- name: Ruff lint
|
|
29
|
+
run: uv run ruff check .
|
|
30
|
+
|
|
31
|
+
- name: Ruff format check
|
|
32
|
+
run: uv run ruff format --check .
|
|
33
|
+
|
|
34
|
+
- name: MyPy
|
|
35
|
+
run: uv run mypy src
|
|
36
|
+
|
|
37
|
+
- name: Pytest
|
|
38
|
+
run: uv run pytest
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
environment:
|
|
15
|
+
name: pypi
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- uses: astral-sh/setup-uv@v6
|
|
21
|
+
|
|
22
|
+
- name: Build package
|
|
23
|
+
run: uv build
|
|
24
|
+
|
|
25
|
+
- name: Publish to PyPI
|
|
26
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# Tests
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
htmlcov/
|
|
16
|
+
|
|
17
|
+
# Type hints
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
|
|
20
|
+
# Vscode
|
|
21
|
+
.vscode
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: local
|
|
3
|
+
hooks:
|
|
4
|
+
- id: ruff-format
|
|
5
|
+
name: Ruff Format
|
|
6
|
+
entry: uv run ruff format
|
|
7
|
+
language: system
|
|
8
|
+
pass_filenames: false
|
|
9
|
+
files: ^(src|tests)/
|
|
10
|
+
|
|
11
|
+
- id: ruff-check
|
|
12
|
+
name: Ruff Check
|
|
13
|
+
entry: uv run ruff check
|
|
14
|
+
language: system
|
|
15
|
+
pass_filenames: false
|
|
16
|
+
files: ^(src|tests)/
|
|
17
|
+
|
|
18
|
+
- id: mypy
|
|
19
|
+
name: MyPy
|
|
20
|
+
entry: uv run mypy src
|
|
21
|
+
language: system
|
|
22
|
+
pass_filenames: false
|
|
23
|
+
files: ^(src|tests)/
|
|
24
|
+
|
|
25
|
+
- id: pytest
|
|
26
|
+
name: Pytest
|
|
27
|
+
entry: uv run pytest
|
|
28
|
+
language: system
|
|
29
|
+
pass_filenames: false
|
|
30
|
+
files: ^(src|tests)/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dsa-study
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: A project to implement various data structures and algorithms in Python.
|
|
5
|
+
Project-URL: Repository, https://github.com/pablohernandezdo/dsa-study-library
|
|
5
6
|
Author: Pablo Hernandez
|
|
6
7
|
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
7
9
|
Requires-Python: >=3.13
|
|
8
10
|
Description-Content-Type: text/markdown
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Dynamic: license-file
|
|
11
11
|
|
|
12
12
|
# DSA Study
|
|
13
13
|
|
|
14
|
+
[](https://github.com/pablohernandezdo/dsa-study-library/actions/workflows/ci.yml)
|
|
15
|
+
|
|
16
|
+
|
|
14
17
|
Educational implementations of common data structures and algorithms written in clean, typed, idiomatic Python.
|
|
15
18
|
|
|
16
19
|
> ⚠️ This project is currently under active development and is primarily intended for learning and educational purposes.
|
|
@@ -114,8 +117,8 @@ src/
|
|
|
114
117
|
|
|
115
118
|
### Linear Structures
|
|
116
119
|
|
|
117
|
-
- [
|
|
118
|
-
- [
|
|
120
|
+
- [X] Linked List
|
|
121
|
+
- [X] Doubly Linked List
|
|
119
122
|
- [ ] Stack
|
|
120
123
|
- [ ] Queue
|
|
121
124
|
- [ ] Priority Queue
|
|
@@ -180,13 +183,14 @@ uv sync
|
|
|
180
183
|
Example:
|
|
181
184
|
|
|
182
185
|
```python
|
|
183
|
-
from dsa.data_structures import
|
|
184
|
-
|
|
185
|
-
uf = UnionFind(["A", "B", "C"])
|
|
186
|
+
from dsa.data_structures.linked_list import LinkedList
|
|
186
187
|
|
|
187
|
-
|
|
188
|
+
ll = LinkedList([1, 2, 3])
|
|
188
189
|
|
|
189
|
-
|
|
190
|
+
ll.insert_front(0) # [0, 1, 2, 3]
|
|
191
|
+
ll.insert_back(4) # [0, 1, 2, 3, 4]
|
|
192
|
+
ll.find(3) # 3
|
|
193
|
+
ll.delete(2) # [0, 1, 3, 4]
|
|
190
194
|
```
|
|
191
195
|
|
|
192
196
|
## Future Work
|
|
@@ -222,4 +226,4 @@ Releases are tracked through:
|
|
|
222
226
|
|
|
223
227
|
- `pyproject.toml`
|
|
224
228
|
- Git tags
|
|
225
|
-
- GitHub releases
|
|
229
|
+
- GitHub releases
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# DSA Study
|
|
2
2
|
|
|
3
|
+
[](https://github.com/pablohernandezdo/dsa-study-library/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
|
|
3
6
|
Educational implementations of common data structures and algorithms written in clean, typed, idiomatic Python.
|
|
4
7
|
|
|
5
8
|
> ⚠️ This project is currently under active development and is primarily intended for learning and educational purposes.
|
|
@@ -103,8 +106,8 @@ src/
|
|
|
103
106
|
|
|
104
107
|
### Linear Structures
|
|
105
108
|
|
|
106
|
-
- [
|
|
107
|
-
- [
|
|
109
|
+
- [X] Linked List
|
|
110
|
+
- [X] Doubly Linked List
|
|
108
111
|
- [ ] Stack
|
|
109
112
|
- [ ] Queue
|
|
110
113
|
- [ ] Priority Queue
|
|
@@ -169,13 +172,14 @@ uv sync
|
|
|
169
172
|
Example:
|
|
170
173
|
|
|
171
174
|
```python
|
|
172
|
-
from dsa.data_structures import
|
|
173
|
-
|
|
174
|
-
uf = UnionFind(["A", "B", "C"])
|
|
175
|
+
from dsa.data_structures.linked_list import LinkedList
|
|
175
176
|
|
|
176
|
-
|
|
177
|
+
ll = LinkedList([1, 2, 3])
|
|
177
178
|
|
|
178
|
-
|
|
179
|
+
ll.insert_front(0) # [0, 1, 2, 3]
|
|
180
|
+
ll.insert_back(4) # [0, 1, 2, 3, 4]
|
|
181
|
+
ll.find(3) # 3
|
|
182
|
+
ll.delete(2) # [0, 1, 3, 4]
|
|
179
183
|
```
|
|
180
184
|
|
|
181
185
|
## Future Work
|
dsa_study-0.4.2/justfile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ruff:
|
|
2
|
+
uv run ruff check src/ tests/
|
|
3
|
+
|
|
4
|
+
mypy:
|
|
5
|
+
uv run mypy src/ tests/
|
|
6
|
+
|
|
7
|
+
test:
|
|
8
|
+
uv run pytest
|
|
9
|
+
|
|
10
|
+
pre-commit:
|
|
11
|
+
uv run pre-commit run --all-files
|
|
12
|
+
|
|
13
|
+
publish:
|
|
14
|
+
uv build
|
|
15
|
+
uv publish --token $(python3 -c "import configparser, pathlib; c = configparser.ConfigParser(); c.read(pathlib.Path.home() / '.pypirc'); print(c['pypi']['password'])")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "dsa-study"
|
|
3
|
+
version = "0.4.2"
|
|
4
|
+
description = "A project to implement various data structures and algorithms in Python."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Pablo Hernandez" }
|
|
11
|
+
]
|
|
12
|
+
dependencies = []
|
|
13
|
+
|
|
14
|
+
[dependency-groups]
|
|
15
|
+
dev = [
|
|
16
|
+
"mypy>=2.1.0",
|
|
17
|
+
"pre-commit>=4.6.0",
|
|
18
|
+
"pytest>=9.0.3",
|
|
19
|
+
"ruff>=0.15.17",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[build-system]
|
|
23
|
+
requires = ["hatchling"]
|
|
24
|
+
build-backend = "hatchling.build"
|
|
25
|
+
|
|
26
|
+
[tool.hatch.build.targets.wheel]
|
|
27
|
+
packages = ["src/dsa"]
|
|
28
|
+
|
|
29
|
+
[tool.mypy]
|
|
30
|
+
python_version = "3.13"
|
|
31
|
+
strict = true
|
|
32
|
+
|
|
33
|
+
[tool.ruff]
|
|
34
|
+
line-length = 88
|
|
35
|
+
|
|
36
|
+
[tool.ruff.lint]
|
|
37
|
+
select = [
|
|
38
|
+
"E",
|
|
39
|
+
"F",
|
|
40
|
+
"I",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Repository = "https://github.com/pablohernandezdo/dsa-study-library"
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterator
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class _Node:
|
|
9
|
+
value: int
|
|
10
|
+
prev: _Node | None = None
|
|
11
|
+
next: _Node | None = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DoublyLinkedList:
|
|
15
|
+
"""
|
|
16
|
+
A doubly linked list of integers.
|
|
17
|
+
|
|
18
|
+
Elements are stored in nodes connected through references to the previous
|
|
19
|
+
and next nodes. This implementation stores both the head and tail pointers.
|
|
20
|
+
|
|
21
|
+
Operations:
|
|
22
|
+
- insert_front: O(1)
|
|
23
|
+
- insert_back: O(1)
|
|
24
|
+
- find: O(n)
|
|
25
|
+
- delete: O(n)
|
|
26
|
+
- clear: O(1)
|
|
27
|
+
- pop_front: O(1)
|
|
28
|
+
- pop_back: O(1)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, values: list[int] | None = None) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Build a doubly linked list from a list of integers.
|
|
34
|
+
|
|
35
|
+
If no values are passed an empty list is created.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
values: Initial values to populate the list.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
if not values:
|
|
42
|
+
self.head = None
|
|
43
|
+
self.tail = None
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
self.head = _Node(values[0])
|
|
47
|
+
prev: _Node = self.head
|
|
48
|
+
|
|
49
|
+
for value in values[1:]:
|
|
50
|
+
prev.next = _Node(value, prev)
|
|
51
|
+
prev = prev.next
|
|
52
|
+
|
|
53
|
+
self.tail = prev
|
|
54
|
+
|
|
55
|
+
def __len__(self) -> int:
|
|
56
|
+
"""
|
|
57
|
+
Return the number of elements in the list.
|
|
58
|
+
|
|
59
|
+
Time Complexity:
|
|
60
|
+
O(n)
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
length = 0
|
|
64
|
+
node = self.head
|
|
65
|
+
|
|
66
|
+
while node:
|
|
67
|
+
length += 1
|
|
68
|
+
node = node.next
|
|
69
|
+
|
|
70
|
+
return length
|
|
71
|
+
|
|
72
|
+
def __iter__(self) -> Iterator[int]:
|
|
73
|
+
"""
|
|
74
|
+
Iterate over the values of the list from head to tail.
|
|
75
|
+
|
|
76
|
+
Time Complexity:
|
|
77
|
+
O(n)
|
|
78
|
+
"""
|
|
79
|
+
node = self.head
|
|
80
|
+
|
|
81
|
+
while node:
|
|
82
|
+
yield node.value
|
|
83
|
+
node = node.next
|
|
84
|
+
|
|
85
|
+
def __reversed__(self) -> Iterator[int]:
|
|
86
|
+
"""
|
|
87
|
+
Iterate over the values of the list from tail to head.
|
|
88
|
+
|
|
89
|
+
Time Complexity:
|
|
90
|
+
O(n)
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
node = self.tail
|
|
94
|
+
|
|
95
|
+
while node:
|
|
96
|
+
yield node.value
|
|
97
|
+
node = node.prev
|
|
98
|
+
|
|
99
|
+
def __repr__(self) -> str:
|
|
100
|
+
return f"DoublyLinkedList({list(self)})"
|
|
101
|
+
|
|
102
|
+
def __str__(self) -> str:
|
|
103
|
+
|
|
104
|
+
if self.head is None:
|
|
105
|
+
return ""
|
|
106
|
+
|
|
107
|
+
representation: str = ""
|
|
108
|
+
|
|
109
|
+
for idx, value in enumerate(self):
|
|
110
|
+
if idx == 0:
|
|
111
|
+
representation += f"{value}"
|
|
112
|
+
else:
|
|
113
|
+
representation += f" <-> {value}"
|
|
114
|
+
|
|
115
|
+
return representation
|
|
116
|
+
|
|
117
|
+
def insert_front(self, value: int) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Insert a value at the beginning of the list.
|
|
120
|
+
|
|
121
|
+
If the list is empty, the inserted node is assigned to both the head and tail.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
value: The value to be inserted.
|
|
125
|
+
|
|
126
|
+
Time Complexity:
|
|
127
|
+
O(1)
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
if self.head is None:
|
|
131
|
+
node = _Node(value)
|
|
132
|
+
self.head = node
|
|
133
|
+
self.tail = node
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
new_node = _Node(value, None, self.head)
|
|
137
|
+
self.head.prev = new_node
|
|
138
|
+
self.head = new_node
|
|
139
|
+
|
|
140
|
+
def insert_back(self, value: int) -> None:
|
|
141
|
+
"""
|
|
142
|
+
Insert a value at the end of the list.
|
|
143
|
+
|
|
144
|
+
If the list is empty, the inserted node is assigned to both the head and tail.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
value: The value to be inserted.
|
|
148
|
+
|
|
149
|
+
Time Complexity:
|
|
150
|
+
O(1)
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
if self.tail is None:
|
|
154
|
+
node = _Node(value)
|
|
155
|
+
self.head = node
|
|
156
|
+
self.tail = node
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
new_node = _Node(value, self.tail, None)
|
|
160
|
+
self.tail.next = new_node
|
|
161
|
+
self.tail = new_node
|
|
162
|
+
|
|
163
|
+
def find(self, value: int) -> int:
|
|
164
|
+
"""
|
|
165
|
+
Return the index of the first occurrence of a value in the list, traversed from
|
|
166
|
+
head to tail.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
value: The value to search for.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
The zero-based index of the value's first occurrence.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
ValueError: If the value is not present in the list.
|
|
176
|
+
|
|
177
|
+
Time Complexity:
|
|
178
|
+
O(n)
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
idx = 0
|
|
182
|
+
node = self.head
|
|
183
|
+
|
|
184
|
+
while node:
|
|
185
|
+
if node.value == value:
|
|
186
|
+
return idx
|
|
187
|
+
|
|
188
|
+
idx += 1
|
|
189
|
+
node = node.next
|
|
190
|
+
|
|
191
|
+
raise ValueError(f"{value} not found in list")
|
|
192
|
+
|
|
193
|
+
def delete(self, value: int) -> None:
|
|
194
|
+
"""
|
|
195
|
+
Delete the first occurrence of a value in the list.
|
|
196
|
+
|
|
197
|
+
If the value is not present the list is left unchanged.
|
|
198
|
+
|
|
199
|
+
If the value appears multiple times, only the first occurrence is removed.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
value: The value to be removed.
|
|
203
|
+
|
|
204
|
+
Time Complexity:
|
|
205
|
+
O(n)
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
if self.head is None:
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
node: _Node | None = self.head
|
|
212
|
+
|
|
213
|
+
while node:
|
|
214
|
+
if node.value == value:
|
|
215
|
+
# deleting head
|
|
216
|
+
if node is self.head:
|
|
217
|
+
# head = tail
|
|
218
|
+
if self.head is self.tail:
|
|
219
|
+
self.head = None
|
|
220
|
+
self.tail = None
|
|
221
|
+
|
|
222
|
+
# other
|
|
223
|
+
else:
|
|
224
|
+
self.head = self.head.next
|
|
225
|
+
|
|
226
|
+
assert self.head is not None
|
|
227
|
+
self.head.prev = None
|
|
228
|
+
|
|
229
|
+
# deleting tail
|
|
230
|
+
elif node is self.tail:
|
|
231
|
+
assert self.tail is not None
|
|
232
|
+
assert self.tail.prev is not None
|
|
233
|
+
|
|
234
|
+
self.tail.prev.next = None
|
|
235
|
+
self.tail = self.tail.prev
|
|
236
|
+
|
|
237
|
+
# deleting in the middle
|
|
238
|
+
else:
|
|
239
|
+
assert node.prev is not None
|
|
240
|
+
assert node.next is not None
|
|
241
|
+
|
|
242
|
+
node.prev.next = node.next
|
|
243
|
+
node.next.prev = node.prev
|
|
244
|
+
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
node = node.next
|
|
248
|
+
|
|
249
|
+
def clear(self) -> None:
|
|
250
|
+
"""
|
|
251
|
+
Remove all nodes from the list.
|
|
252
|
+
|
|
253
|
+
Time Complexity:
|
|
254
|
+
O(1)
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
self.head = None
|
|
258
|
+
self.tail = None
|
|
259
|
+
|
|
260
|
+
def pop_front(self) -> int:
|
|
261
|
+
"""
|
|
262
|
+
Remove the element at the head of the list.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
The value of the element at the head of the list.
|
|
266
|
+
|
|
267
|
+
Raises:
|
|
268
|
+
IndexError: If the list is empty.
|
|
269
|
+
|
|
270
|
+
Time Complexity:
|
|
271
|
+
O(1)
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
if self.head is None:
|
|
275
|
+
raise IndexError("pop from empty list")
|
|
276
|
+
|
|
277
|
+
if self.head is self.tail:
|
|
278
|
+
value = self.head.value
|
|
279
|
+
self.head = None
|
|
280
|
+
self.tail = None
|
|
281
|
+
return value
|
|
282
|
+
|
|
283
|
+
first_value: int = self.head.value
|
|
284
|
+
|
|
285
|
+
assert self.head.next is not None
|
|
286
|
+
self.head.next.prev = None
|
|
287
|
+
self.head = self.head.next
|
|
288
|
+
|
|
289
|
+
return first_value
|
|
290
|
+
|
|
291
|
+
def pop_back(self) -> int:
|
|
292
|
+
"""
|
|
293
|
+
Remove the element at the tail of the list.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
The value of the element at the tail of the list.
|
|
297
|
+
|
|
298
|
+
Raises:
|
|
299
|
+
IndexError: If the list is empty.
|
|
300
|
+
|
|
301
|
+
Time Complexity:
|
|
302
|
+
O(1)
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
if self.tail is None:
|
|
306
|
+
raise IndexError("pop from empty list")
|
|
307
|
+
|
|
308
|
+
if self.head is self.tail:
|
|
309
|
+
value = self.tail.value
|
|
310
|
+
self.head = None
|
|
311
|
+
self.tail = None
|
|
312
|
+
return value
|
|
313
|
+
|
|
314
|
+
last_value: int = self.tail.value
|
|
315
|
+
|
|
316
|
+
assert self.tail.prev is not None
|
|
317
|
+
self.tail.prev.next = None
|
|
318
|
+
self.tail = self.tail.prev
|
|
319
|
+
|
|
320
|
+
return last_value
|