Skip to content

Commit 013f294

Browse files
authored
Merge branch 'master' into fix-invalid-assignment
2 parents 9555b93 + e49caf5 commit 013f294

6 files changed

Lines changed: 188 additions & 13 deletions

File tree

‎DIRECTORY.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,9 @@
276276
* [Permutations](data_structures/arrays/permutations.py)
277277
* [Prefix Sum](data_structures/arrays/prefix_sum.py)
278278
* [Product Sum](data_structures/arrays/product_sum.py)
279+
* [Reverse Array](data_structures/arrays/reverse_array.py)
279280
* [Rotate Array](data_structures/arrays/rotate_array.py)
281+
* [Set Matrix Zeroes](data_structures/arrays/set_matrix_zeroes.py)
280282
* [Sparse Table](data_structures/arrays/sparse_table.py)
281283
* [Sudoku Solver](data_structures/arrays/sudoku_solver.py)
282284
* Binary Tree
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""
2+
In-place array reversal that also returns the reversed list to the caller.
3+
This algorithm reverses the elements of a list without using extra space.
4+
"""
5+
6+
from typing import Any
7+
8+
9+
def reverse_array(arr: list[Any]) -> list[Any]:
10+
"""
11+
Reverses a list in-place.
12+
13+
This function takes a list and reverses its elements using a two-pointer
14+
approach. The left pointer starts at the beginning of the list, and the
15+
right pointer starts at the end. The elements at these two pointers are
16+
swapped, and the pointers move towards the center until they meet or cross.
17+
18+
Args:
19+
arr: The list to be reversed.
20+
21+
Returns:
22+
The same list, now reversed. This allows for method chaining.
23+
24+
Doctests:
25+
>>> reverse_array([1, 2, 3, 4, 5])
26+
[5, 4, 3, 2, 1]
27+
>>> reverse_array(['a', 'b', 'c', 'd'])
28+
['d', 'c', 'b', 'a']
29+
>>> reverse_array([10.5, 20.2, 30.8])
30+
[30.8, 20.2, 10.5]
31+
>>> reverse_array(["apple", "banana", "cherry"])
32+
['cherry', 'banana', 'apple']
33+
>>> reverse_array([1])
34+
[1]
35+
>>> reverse_array([])
36+
[]
37+
>>> reverse_array(list(range(5)))
38+
[4, 3, 2, 1, 0]
39+
>>> reverse_array(tuple(range(5)))
40+
Traceback (most recent call last):
41+
...
42+
TypeError: 'tuple' object does not support item assignment
43+
>>> reverse_array(range(5))
44+
Traceback (most recent call last):
45+
...
46+
TypeError: 'range' object does not support item assignment
47+
"""
48+
left = 0
49+
right = len(arr) - 1
50+
51+
while left < right:
52+
# Swap the elements at the left and right pointers
53+
arr[left], arr[right] = arr[right], arr[left]
54+
# Move the pointers towards the center
55+
left += 1
56+
right -= 1
57+
return arr
58+
59+
60+
if __name__ == "__main__":
61+
# The doctest module runs the tests embedded in the function's docstring.
62+
# To run the tests, execute this script from the command line:
63+
# python -m doctest -v reverse_array.py
64+
import doctest
65+
66+
doctest.testmod()
67+
68+
# Example usage:
69+
print("\n--- Example Usage ---")
70+
sample_array = [10, 20, 30, 40, 50, 60]
71+
print(f"{sample_array = }")
72+
print(f"{reverse_array(sample_array) = }")
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
Set Matrix Zeroes Algorithm
3+
---------------------------
4+
If an element in an m x n matrix is 0, set its entire row and column to 0.
5+
6+
Explanation:
7+
We use the first row and first column as markers to track which rows and
8+
columns should be zeroed, avoiding extra space usage (O(1) space complexity).
9+
10+
References:
11+
https://leetcode.com/problems/set-matrix-zeroes/
12+
13+
Doctest:
14+
>>> matrix = [
15+
... [1, 1, 1],
16+
... [1, 0, 1],
17+
... [1, 1, 1]
18+
... ]
19+
>>> set_matrix_zeroes(matrix)
20+
>>> matrix
21+
[[1, 0, 1], [0, 0, 0], [1, 0, 1]]
22+
>>> matrix = [[0, 1, 2, 0], [3, 4, 5, 2], [1, 3, 1, 5]]
23+
>>> set_matrix_zeroes(matrix)
24+
>>> matrix
25+
[[0, 0, 0, 0], [0, 4, 5, 0], [0, 3, 1, 0]]
26+
"""
27+
28+
29+
def set_matrix_zeroes(matrix: list[list[int]]) -> None:
30+
"""
31+
Modify the matrix in-place such that if an element is 0,
32+
its entire row and column are set to 0.
33+
34+
:param matrix: 2D list of integers
35+
:return: None (modifies matrix in-place)
36+
37+
Time Complexity: O(m * n)
38+
Space Complexity: O(1)
39+
"""
40+
rows = len(matrix)
41+
cols = len(matrix[0])
42+
col0 = 1
43+
44+
# Step 1: Mark rows and columns that need to be zeroed
45+
for i in range(rows):
46+
if matrix[i][0] == 0:
47+
col0 = 0
48+
for j in range(1, cols):
49+
if matrix[i][j] == 0:
50+
matrix[i][0] = 0
51+
matrix[0][j] = 0
52+
53+
# Step 2: Update the inner matrix cells
54+
for i in range(1, rows):
55+
for j in range(1, cols):
56+
if matrix[i][0] == 0 or matrix[0][j] == 0:
57+
matrix[i][j] = 0
58+
59+
# Step 3: Handle the first row
60+
if matrix[0][0] == 0:
61+
for j in range(cols):
62+
matrix[0][j] = 0
63+
64+
# Step 4: Handle the first column
65+
if col0 == 0:
66+
for i in range(rows):
67+
matrix[i][0] = 0

‎sorts/quick_sort_3_partition.py‎

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
from random import randrange
2+
from typing import Any, Protocol
23

34

4-
def quick_sort_3partition(sorting: list, left: int, right: int) -> None:
5+
class Comparable(Protocol):
6+
def __lt__(self, other: Any, /) -> bool: ...
7+
8+
9+
def quick_sort_3partition[T: Comparable](
10+
sorting: list[T], left: int, right: int
11+
) -> None:
512
""" "
613
Python implementation of the quicksort algorithm with 3-way partition.
714
The idea of 3-way quicksort is based on "Dutch National Flag algorithm".
@@ -44,7 +51,9 @@ def quick_sort_3partition(sorting: list, left: int, right: int) -> None:
4451
quick_sort_3partition(sorting, b + 1, right)
4552

4653

47-
def quick_sort_lomuto_partition(sorting: list, left: int, right: int) -> None:
54+
def quick_sort_lomuto_partition[T: Comparable](
55+
sorting: list[T], left: int, right: int
56+
) -> None:
4857
"""
4958
A pure Python implementation of the quicksort algorithm(in-place)
5059
with Lomuto partition scheme:
@@ -75,7 +84,7 @@ def quick_sort_lomuto_partition(sorting: list, left: int, right: int) -> None:
7584
quick_sort_lomuto_partition(sorting, pivot_index + 1, right)
7685

7786

78-
def lomuto_partition(sorting: list, left: int, right: int) -> int:
87+
def lomuto_partition[T: Comparable](sorting: list[T], left: int, right: int) -> int:
7988
"""
8089
Example:
8190
>>> lomuto_partition([1,5,7,6], 0, 3)
@@ -91,8 +100,8 @@ def lomuto_partition(sorting: list, left: int, right: int) -> int:
91100
return store_index
92101

93102

94-
def hoare_partition_by_value(
95-
array: list, pivot_value: int, start: int = 0, end: int | None = None
103+
def hoare_partition_by_value[T: Comparable](
104+
array: list[T], pivot_value: T, start: int = 0, end: int | None = None
96105
) -> int:
97106
"""
98107
Returns the starting index of the right subarray, which contains the
@@ -172,8 +181,8 @@ def hoare_partition_by_value(
172181
return right + 1
173182

174183

175-
def hoare_partition_by_pivot(
176-
array: list, pivot_index: int, start=0, end: int | None = None
184+
def hoare_partition_by_pivot[T: Comparable](
185+
array: list[T], pivot_index: int, start=0, end: int | None = None
177186
) -> int:
178187
"""
179188
Returns the new pivot index after partitioning
@@ -201,7 +210,9 @@ def swap(i1, i2):
201210
return greater_or_equal
202211

203212

204-
def quicksort_hoare(array: list, start: int = 0, end: int | None = None):
213+
def quicksort_hoare[T: Comparable](
214+
array: list[T], start: int = 0, end: int | None = None
215+
) -> None:
205216
"""
206217
Quicksort using the Hoare partition scheme:
207218
- https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme
@@ -225,7 +236,7 @@ def quicksort_hoare(array: list, start: int = 0, end: int | None = None):
225236
quicksort_hoare(array, pivot_index_final + 1, end)
226237

227238

228-
def three_way_radix_quicksort(sorting: list) -> list:
239+
def three_way_radix_quicksort[T: Comparable](sorting: list[T]) -> list[T]:
229240
"""
230241
Three-way radix quicksort:
231242
https://en.wikipedia.org/wiki/Quicksort#Three-way_radix_quicksort

‎sorts/reverse_selection.py‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@
44
This algorithm progressively sorts the array by reversing subarrays
55
66
For doctests run following command:
7-
python3 -m doctest -v reverse_selection_sort.py
7+
python3 -m doctest -v reverse_selection.py
88
99
For manual testing run:
10-
python3 reverse_selection_sort.py
10+
python3 reverse_selection.py
1111
"""
1212

13+
from typing import Any, Protocol
1314

14-
def reverse_subarray(arr: list, start: int, end: int) -> None:
15+
16+
class Comparable(Protocol):
17+
def __lt__(self, other: Any, /) -> bool: ...
18+
19+
20+
def reverse_subarray[T](arr: list[T], start: int, end: int) -> None:
1521
"""
1622
Reverse a subarray in-place.
1723
@@ -41,7 +47,7 @@ def reverse_subarray(arr: list, start: int, end: int) -> None:
4147
end -= 1
4248

4349

44-
def reverse_selection_sort(collection: list) -> list:
50+
def reverse_selection_sort[T: Comparable](collection: list[T]) -> list[T]:
4551
"""
4652
A pure implementation of reverse selection sort algorithm in Python
4753
@@ -64,6 +70,17 @@ def reverse_selection_sort(collection: list) -> list:
6470
6571
>>> reverse_selection_sort([5, 4, 3, 2, 1])
6672
[1, 2, 3, 4, 5]
73+
74+
>>> reverse_selection_sort(["banana", "apple", "cherry"])
75+
['apple', 'banana', 'cherry']
76+
77+
>>> reverse_selection_sort([3.14, 1.5, 2.7])
78+
[1.5, 2.7, 3.14]
79+
80+
>>> reverse_selection_sort([1, "a"]) # doctest: +ELLIPSIS
81+
Traceback (most recent call last):
82+
...
83+
TypeError: ...
6784
"""
6885
n = len(collection)
6986
for i in range(n - 1):

‎tests/test_sorts.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@
4343
from sorts.pancake_sort import pancake_sort
4444
from sorts.patience_sort import patience_sort
4545
from sorts.quick_sort import quick_sort
46+
from sorts.quick_sort_3_partition import three_way_radix_quicksort
4647
from sorts.recursive_insertion_sort import rec_insertion_sort
4748
from sorts.recursive_mergesort_array import merge
49+
from sorts.reverse_selection import reverse_selection_sort
4850
from sorts.reversort import reversort
4951
from sorts.selection_sort import selection_sort
5052
from sorts.shell_sort import shell_sort
@@ -85,12 +87,14 @@ def test_heap_sort() -> None:
8587
pancake_sort,
8688
patience_sort,
8789
quick_sort,
90+
reverse_selection_sort,
8891
reversort,
8992
selection_sort,
9093
shell_sort,
9194
shrink_shell_sort,
9295
stooge_sort,
9396
strand_sort,
97+
three_way_radix_quicksort,
9498
tim_sort,
9599
unknown_sort,
96100
)
@@ -161,10 +165,12 @@ def test_rec_insertion_sort(case) -> None:
161165
odd_even_transposition,
162166
pancake_sort,
163167
patience_sort,
168+
reverse_selection_sort,
164169
reversort,
165170
selection_sort,
166171
shrink_shell_sort,
167172
strand_sort,
173+
three_way_radix_quicksort,
168174
tim_sort,
169175
unknown_sort,
170176
],

0 commit comments

Comments
 (0)