dsa-study 0.2.0__tar.gz → 0.4.4__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.4/.github/workflows/ci.yml +38 -0
- dsa_study-0.4.4/.github/workflows/publish.yml +29 -0
- {dsa_study-0.2.0 → dsa_study-0.4.4}/.gitignore +3 -0
- dsa_study-0.4.4/.pre-commit-config.yaml +30 -0
- dsa_study-0.2.0/README.md → dsa_study-0.4.4/PKG-INFO +28 -2
- dsa_study-0.2.0/PKG-INFO → dsa_study-0.4.4/README.md +13 -13
- dsa_study-0.4.4/justfile +21 -0
- {dsa_study-0.2.0 → dsa_study-0.4.4}/pyproject.toml +27 -2
- dsa_study-0.4.4/src/dsa/data_structures/doubly_linked_list.py +320 -0
- {dsa_study-0.2.0 → dsa_study-0.4.4}/src/dsa/data_structures/linked_list.py +7 -3
- dsa_study-0.4.4/tests/data_structures/test_doubly_linked_list.py +270 -0
- {dsa_study-0.2.0 → dsa_study-0.4.4}/tests/data_structures/test_linked_list.py +29 -29
- dsa_study-0.4.4/uv.lock +486 -0
- dsa_study-0.2.0/algorithms/al01_binary_search.py +0 -60
- dsa_study-0.2.0/algorithms/al02_selection_sort.py +0 -60
- dsa_study-0.2.0/algorithms/al03_quicksort.py +0 -138
- dsa_study-0.2.0/algorithms/al04_bfs.py +0 -55
- dsa_study-0.2.0/algorithms/al05_dfs.py +0 -52
- dsa_study-0.2.0/algorithms/al06_dijkstra.py +0 -193
- dsa_study-0.2.0/algorithms/al07_tree.py +0 -545
- dsa_study-0.2.0/algorithms/al08_bst.py +0 -239
- dsa_study-0.2.0/algorithms/al09_heap.py +0 -148
- dsa_study-0.2.0/algorithms/al10_dijkstra_heap.py +0 -175
- dsa_study-0.2.0/algorithms/al11_topological_sort.py +0 -143
- dsa_study-0.2.0/algorithms/al12_kruskal.py +0 -123
- dsa_study-0.2.0/algorithms/al13_prim.py +0 -73
- dsa_study-0.2.0/algorithms/bubblesort.py +0 -30
- dsa_study-0.2.0/data_structures/ds01_array.py +0 -0
- dsa_study-0.2.0/data_structures/ds02_linked_list.py +0 -0
- dsa_study-0.2.0/data_structures/ds03_union_find.py +0 -91
- dsa_study-0.2.0/dsa_legacy/binary_search.py +0 -35
- dsa_study-0.2.0/dsa_legacy/bs.py +0 -37
- dsa_study-0.2.0/dsa_legacy/linked_list.py +0 -44
- dsa_study-0.2.0/dsa_legacy/recursion.py +0 -42
- dsa_study-0.2.0/dsa_legacy/selection_sort.py +0 -32
- dsa_study-0.2.0/justfile +0 -6
- dsa_study-0.2.0/neetcode/arrays_&_hasing/1. contains_duplicate.py +0 -60
- dsa_study-0.2.0/neetcode/arrays_&_hasing/2. valid_anagram.py +0 -108
- dsa_study-0.2.0/neetcode/arrays_&_hasing/3. two_sum.py +0 -67
- dsa_study-0.2.0/neetcode/arrays_&_hasing/4. group_anagrams.py +0 -70
- dsa_study-0.2.0/uv.lock +0 -79
- {dsa_study-0.2.0 → dsa_study-0.4.4}/.python-version +0 -0
- {dsa_study-0.2.0 → dsa_study-0.4.4}/LICENSE +0 -0
- {dsa_study-0.2.0 → dsa_study-0.4.4}/src/dsa/__init__.py +0 -0
- {dsa_study-0.2.0 → dsa_study-0.4.4}/src/dsa/algorithms/__init__.py +0 -0
- {dsa_study-0.2.0 → dsa_study-0.4.4}/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 3.13
|
|
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,29 @@
|
|
|
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: Set up Python
|
|
23
|
+
run: uv python install 3.13
|
|
24
|
+
|
|
25
|
+
- name: Build package
|
|
26
|
+
run: uv build
|
|
27
|
+
|
|
28
|
+
- name: Publish to PyPI
|
|
29
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -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)/
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dsa-study
|
|
3
|
+
Version: 0.4.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
|
|
6
|
+
Author: Pablo Hernandez
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Typing :: Typed
|
|
13
|
+
Requires-Python: <3.14,>=3.13
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# DSA Learning Library
|
|
17
|
+
|
|
18
|
+
[](https://github.com/pablohernandezdo/dsa-study-library/actions/workflows/ci.yml)
|
|
19
|
+
[](https://pypi.org/project/dsa-study/)
|
|
20
|
+
[](https://pypi.org/project/dsa-study/)
|
|
21
|
+
[](https://github.com/pablohernandezdo/dsa-study-library/blob/main/LICENSE)
|
|
2
22
|
|
|
3
23
|
Educational implementations of common data structures and algorithms written in clean, typed, idiomatic Python.
|
|
4
24
|
|
|
@@ -104,7 +124,7 @@ src/
|
|
|
104
124
|
### Linear Structures
|
|
105
125
|
|
|
106
126
|
- [X] Linked List
|
|
107
|
-
- [
|
|
127
|
+
- [X] Doubly Linked List
|
|
108
128
|
- [ ] Stack
|
|
109
129
|
- [ ] Queue
|
|
110
130
|
- [ ] Priority Queue
|
|
@@ -158,6 +178,12 @@ Every implementation should include tests covering:
|
|
|
158
178
|
|
|
159
179
|
## Usage
|
|
160
180
|
|
|
181
|
+
Installation:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
pip install dsa-study
|
|
185
|
+
```
|
|
186
|
+
|
|
161
187
|
Installation from source:
|
|
162
188
|
|
|
163
189
|
```bash
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
License-Expression: MIT
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Requires-Python: >=3.13
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
|
|
12
|
-
# DSA Study
|
|
1
|
+
# DSA Learning Library
|
|
2
|
+
|
|
3
|
+
[](https://github.com/pablohernandezdo/dsa-study-library/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/dsa-study/)
|
|
5
|
+
[](https://pypi.org/project/dsa-study/)
|
|
6
|
+
[](https://github.com/pablohernandezdo/dsa-study-library/blob/main/LICENSE)
|
|
13
7
|
|
|
14
8
|
Educational implementations of common data structures and algorithms written in clean, typed, idiomatic Python.
|
|
15
9
|
|
|
@@ -115,7 +109,7 @@ src/
|
|
|
115
109
|
### Linear Structures
|
|
116
110
|
|
|
117
111
|
- [X] Linked List
|
|
118
|
-
- [
|
|
112
|
+
- [X] Doubly Linked List
|
|
119
113
|
- [ ] Stack
|
|
120
114
|
- [ ] Queue
|
|
121
115
|
- [ ] Priority Queue
|
|
@@ -169,6 +163,12 @@ Every implementation should include tests covering:
|
|
|
169
163
|
|
|
170
164
|
## Usage
|
|
171
165
|
|
|
166
|
+
Installation:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
pip install dsa-study
|
|
170
|
+
```
|
|
171
|
+
|
|
172
172
|
Installation from source:
|
|
173
173
|
|
|
174
174
|
```bash
|
dsa_study-0.4.4/justfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
cov:
|
|
11
|
+
uv run pytest --cov=dsa --cov-report=term-missing
|
|
12
|
+
|
|
13
|
+
cov-html:
|
|
14
|
+
uv run pytest --cov=dsa --cov-report=html
|
|
15
|
+
|
|
16
|
+
pre-commit:
|
|
17
|
+
uv run pre-commit run --all-files
|
|
18
|
+
|
|
19
|
+
publish:
|
|
20
|
+
uv build
|
|
21
|
+
uv publish --token $(python3 -c "import configparser, pathlib; c = configparser.ConfigParser(); c.read(pathlib.Path.home() / '.pypirc'); print(c['pypi']['password'])")
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dsa-study"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.4"
|
|
4
4
|
description = "A project to implement various data structures and algorithms in Python."
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.13"
|
|
6
|
+
requires-python = ">=3.13,<3.14"
|
|
7
7
|
license = "MIT"
|
|
8
8
|
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Programming Language :: Python :: 3",
|
|
11
|
+
"Programming Language :: Python :: 3.13",
|
|
12
|
+
"Typing :: Typed",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
]
|
|
15
|
+
|
|
9
16
|
authors = [
|
|
10
17
|
{ name = "Pablo Hernandez" }
|
|
11
18
|
]
|
|
@@ -13,7 +20,11 @@ dependencies = []
|
|
|
13
20
|
|
|
14
21
|
[dependency-groups]
|
|
15
22
|
dev = [
|
|
23
|
+
"mypy>=2.1.0",
|
|
24
|
+
"pre-commit>=4.6.0",
|
|
16
25
|
"pytest>=9.0.3",
|
|
26
|
+
"pytest-cov>=7.1.0",
|
|
27
|
+
"ruff>=0.15.17",
|
|
17
28
|
]
|
|
18
29
|
|
|
19
30
|
[build-system]
|
|
@@ -23,5 +34,19 @@ build-backend = "hatchling.build"
|
|
|
23
34
|
[tool.hatch.build.targets.wheel]
|
|
24
35
|
packages = ["src/dsa"]
|
|
25
36
|
|
|
37
|
+
[tool.mypy]
|
|
38
|
+
python_version = "3.13"
|
|
39
|
+
strict = true
|
|
40
|
+
|
|
41
|
+
[tool.ruff]
|
|
42
|
+
line-length = 88
|
|
43
|
+
|
|
44
|
+
[tool.ruff.lint]
|
|
45
|
+
select = [
|
|
46
|
+
"E",
|
|
47
|
+
"F",
|
|
48
|
+
"I",
|
|
49
|
+
]
|
|
50
|
+
|
|
26
51
|
[project.urls]
|
|
27
52
|
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
|
|
@@ -93,6 +93,8 @@ class LinkedList:
|
|
|
93
93
|
"""
|
|
94
94
|
Insert a value at the beginning of the list.
|
|
95
95
|
|
|
96
|
+
If the list is empty, the inserted node is assigned to the head.
|
|
97
|
+
|
|
96
98
|
Args:
|
|
97
99
|
value: The value to be inserted.
|
|
98
100
|
|
|
@@ -109,6 +111,8 @@ class LinkedList:
|
|
|
109
111
|
"""
|
|
110
112
|
Insert a value at the end of the list.
|
|
111
113
|
|
|
114
|
+
If the list is empty, the inserted node is assigned to the head.
|
|
115
|
+
|
|
112
116
|
Args:
|
|
113
117
|
value: The value to be inserted.
|
|
114
118
|
|
|
@@ -133,7 +137,7 @@ class LinkedList:
|
|
|
133
137
|
value: Value to be found.
|
|
134
138
|
|
|
135
139
|
Returns:
|
|
136
|
-
The zero-based index of the value.
|
|
140
|
+
The zero-based index of the value's first occurrence.
|
|
137
141
|
|
|
138
142
|
Raises:
|
|
139
143
|
ValueError: If the value is not present in the list.
|
|
@@ -171,8 +175,8 @@ class LinkedList:
|
|
|
171
175
|
if self.head is None:
|
|
172
176
|
return
|
|
173
177
|
|
|
174
|
-
prev = None
|
|
175
|
-
current = self.head
|
|
178
|
+
prev: _Node | None = None
|
|
179
|
+
current: _Node | None = self.head
|
|
176
180
|
|
|
177
181
|
while current:
|
|
178
182
|
if current.value == value:
|