-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
python practice disguised as linked list problems
- Loading branch information
Anurag Saini
committed
Sep 7, 2024
1 parent
5d42da6
commit 627c4e4
Showing
45 changed files
with
5,929 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Clone linked lists | ||
|
||
<style> | ||
.md-logo img { | ||
content: url('/data-structures/linked-list/polyline-light.svg'); | ||
} | ||
|
||
:root [data-md-color-scheme=slate] .md-logo img { | ||
content: url('/data-structures/linked-list/polyline-night.svg'); | ||
} | ||
</style> | ||
|
||
## Shallow copy | ||
|
||
```python linenums="1" | ||
def __copy__(self): | ||
copy = LinkedList() | ||
for e in self: | ||
copy.append(e) | ||
return copy | ||
``` | ||
|
||
## Unit tests | ||
|
||
```python linenums="1" | ||
def test_clone(): | ||
l = LinkedList() | ||
assert copy(l) == l | ||
assert copy(l) is not l | ||
|
||
l = LinkedList() | ||
l.prepend(3) | ||
l.prepend(2) | ||
l.prepend(1) | ||
assert copy(l) == l | ||
assert copy(l) is not l | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Compare linked lists | ||
|
||
<style> | ||
.md-logo img { | ||
content: url('/data-structures/linked-list/polyline-light.svg'); | ||
} | ||
|
||
:root [data-md-color-scheme=slate] .md-logo img { | ||
content: url('/data-structures/linked-list/polyline-night.svg'); | ||
} | ||
</style> | ||
|
||
## Override `__eq__` | ||
|
||
```python linenums="1" | ||
def __eq__(self, other: 'LinkedList'): | ||
if self is other: | ||
return True | ||
|
||
cursor = self._sentinel.next | ||
for e in other: | ||
if e != cursor.value: | ||
return False | ||
cursor = cursor.next | ||
|
||
return cursor is self._sentinel | ||
``` | ||
|
||
## Unit tests | ||
|
||
```python linenums="1" | ||
def test_equality_empty(): | ||
assert LinkedList() == LinkedList() | ||
assert LinkedList() is not LinkedList() | ||
|
||
|
||
def test_equality_simple(): | ||
p = LinkedList() | ||
p.append(1) | ||
p.append(2) | ||
p.append(3) | ||
|
||
q = LinkedList() | ||
q.append(1) | ||
q.append(2) | ||
q.append(3) | ||
assert p == q | ||
assert p is not q | ||
|
||
|
||
def test_equality_partial(): | ||
p = LinkedList() | ||
p.append(1) | ||
p.append(2) | ||
p.append(3) | ||
p.append(4) | ||
|
||
q = LinkedList() | ||
q.append(1) | ||
q.append(2) | ||
q.append(3) | ||
|
||
assert p != q | ||
|
||
q.append(4) | ||
assert p == q | ||
|
||
q.append(5) | ||
assert p != q | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# Iterate over linked list | ||
|
||
<style> | ||
.md-logo img { | ||
content: url('/data-structures/linked-list/polyline-light.svg'); | ||
} | ||
|
||
:root [data-md-color-scheme=slate] .md-logo img { | ||
content: url('/data-structures/linked-list/polyline-night.svg'); | ||
} | ||
</style> | ||
|
||
## Get item | ||
|
||
=== "Implementation" | ||
|
||
```python linenums="1" title="list.py" | ||
def __getitem__(self, index) -> Optional[int]: | ||
i, cursor = 0, self._sentinel.next | ||
while i <= index: | ||
if cursor is self._sentinel: | ||
return None | ||
if i == index: | ||
return cursor.value | ||
i, cursor = i+1, cursor.next | ||
``` | ||
|
||
=== "Unit tests" | ||
|
||
```python linenums="1" | ||
def test_get(): | ||
ll = LinkedList() | ||
ll.append(1) | ||
ll.append(2) | ||
ll.append(3) | ||
ll.append(4) | ||
ll.append(5) | ||
|
||
assert ll[0] == 1 | ||
assert ll[1] == 2 | ||
assert ll[2] == 3 | ||
assert ll[3] == 4 | ||
assert ll[4] == 5 | ||
assert ll[5] is None | ||
``` | ||
|
||
## Iterator | ||
|
||
=== "Implementation" | ||
|
||
```python linenums="1" title="list.py" | ||
def __iter__(self): | ||
cursor = self._sentinel.next | ||
while cursor is not self._sentinel: | ||
yield cursor.value | ||
cursor = cursor.next | ||
``` | ||
|
||
=== "Unit tests" | ||
|
||
```python linenums="1" | ||
def test_iteration(): | ||
ll = LinkedList() | ||
ll.append(1) | ||
ll.append(2) | ||
ll.append(3) | ||
ll.append(4) | ||
ll.append(5) | ||
|
||
for i, v in enumerate(ll): | ||
assert v == ll[i] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
# Merge linked lists | ||
|
||
<style> | ||
.md-logo img { | ||
content: url('/data-structures/linked-list/polyline-light.svg'); | ||
} | ||
|
||
:root [data-md-color-scheme=slate] .md-logo img { | ||
content: url('/data-structures/linked-list/polyline-night.svg'); | ||
} | ||
</style> | ||
|
||
## In-place merging of sorted linked lists | ||
|
||
```python linenums="1" | ||
def sorted_ingest(self, other: 'LinkedList'): | ||
if self is other: | ||
return | ||
|
||
new_sentinel = Node(0) # (1)! | ||
cursor = new_sentinel | ||
|
||
cursor_self = self._sentinel.next | ||
cursor_other = other._sentinel.next | ||
|
||
while cursor_self is not self._sentinel and cursor_other is not other._sentinel: # (2)! | ||
if cursor_self.value <= cursor_other.value: | ||
cursor.next = cursor_self | ||
cursor_self = cursor_self.next | ||
else: | ||
cursor.next = cursor_other | ||
cursor_other = cursor_other.next | ||
cursor = cursor.next | ||
|
||
if cursor_self is not self._sentinel: | ||
cursor.next = cursor_self # (3)! | ||
new_sentinel.prev = self._sentinel.prev # (4)! | ||
elif cursor_other is not other._sentinel: | ||
cursor.next = cursor_other | ||
new_sentinel.prev = other._sentinel.prev | ||
else: | ||
new_sentinel.prev = cursor # (5)! | ||
|
||
self._sentinel = new_sentinel # (6)! | ||
other._sentinel.next = other._sentinel # (7)! | ||
|
||
``` | ||
|
||
1. Set up head for merged list | ||
2. Merge until either list exhausts. | ||
3. No need to loop here, we can just link the remaining list. | ||
4. We don't need to find the `tail` of the remaining list. It'll be the `prev` of original sentinel. | ||
5. Doing the same work as line 22 and 25. | ||
6. Stitch the two ends of new list. | ||
7. Leave the ingested list empty. | ||
|
||
## Unit tests | ||
|
||
```python linenums="1" | ||
def test_merge_empty(): | ||
a = LinkedList() | ||
b = LinkedList() | ||
a.sorted_ingest(b) | ||
|
||
assert str(a) == "[]" | ||
assert b.empty | ||
|
||
|
||
def test_merge_empty_in_single(): | ||
a = LinkedList() | ||
a.append(1) | ||
b = LinkedList() | ||
a.sorted_ingest(b) | ||
|
||
assert str(a) == "[1]" | ||
assert b.empty | ||
|
||
|
||
def test_merge_single_in_empty(): | ||
a = LinkedList() | ||
b = LinkedList() | ||
b.append(1) | ||
a.sorted_ingest(b) | ||
|
||
assert str(a) == "[1]" | ||
assert b.empty | ||
|
||
|
||
def test_merge_is_append(): | ||
a = LinkedList() | ||
a.append(1) | ||
a.append(2) | ||
a.append(3) | ||
b = LinkedList() | ||
b.append(4) | ||
b.append(5) | ||
b.append(6) | ||
a.sorted_ingest(b) | ||
|
||
assert str(a) == "[1, 2, 3, 4, 5, 6]" | ||
assert b.empty | ||
|
||
|
||
def test_merge_is_prepend(): | ||
a = LinkedList() | ||
a.append(1) | ||
a.append(2) | ||
a.append(3) | ||
b = LinkedList() | ||
b.append(4) | ||
b.append(5) | ||
b.append(6) | ||
b.sorted_ingest(a) | ||
|
||
assert str(b) == "[1, 2, 3, 4, 5, 6]" | ||
assert a.empty | ||
|
||
|
||
def test_merge_is_interleave(): | ||
a = LinkedList() | ||
a.append(1) | ||
a.append(3) | ||
a.append(5) | ||
b = LinkedList() | ||
b.append(2) | ||
b.append(4) | ||
b.append(6) | ||
b.sorted_ingest(a) | ||
|
||
assert str(b) == "[1, 2, 3, 4, 5, 6]" | ||
assert a.empty | ||
|
||
|
||
def test_merge_is_interleave_uneven(): | ||
a = LinkedList() | ||
a.append(1) | ||
a.append(3) | ||
a.append(5) | ||
a.append(10) | ||
b = LinkedList() | ||
b.append(2) | ||
b.append(4) | ||
b.append(6) | ||
b.sorted_ingest(a) | ||
|
||
assert str(b) == "[1, 2, 3, 4, 5, 6, 10]" | ||
assert a.empty | ||
|
||
|
||
def test_merge_random(): | ||
a = sorted([random.randint(0, 100) for x in range(0, 10)]) | ||
b = sorted([random.randint(0, 100) for x in range(0, 10)]) | ||
c = sorted(a + b) | ||
|
||
p = LinkedList() | ||
for e in a: | ||
p.append(e) | ||
|
||
q = LinkedList() | ||
for e in b: | ||
q.append(e) | ||
|
||
p.sorted_ingest(q) | ||
|
||
for i, e in enumerate(p): | ||
assert e == c[i] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.