Skip to content

Commit 3026508

Browse files
authored
Merge branch 'master' into feature/recursive-quick-sort-comparable-typing
2 parents 792887f + e49caf5 commit 3026508

7 files changed

Lines changed: 265 additions & 13 deletions

File tree

‎DIRECTORY.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,16 @@
269269
* [Index 2D Array In 1D](data_structures/arrays/index_2d_array_in_1d.py)
270270
* [Kth Largest Element](data_structures/arrays/kth_largest_element.py)
271271
* [Median Two Array](data_structures/arrays/median_two_array.py)
272+
* [Merge Sorted](data_structures/arrays/merge_sorted.py)
272273
* [Monotonic Array](data_structures/arrays/monotonic_array.py)
273274
* [Pairs With Given Sum](data_structures/arrays/pairs_with_given_sum.py)
274275
* [Pairwise Iteration](data_structures/arrays/pairwise_iteration.py)
275276
* [Permutations](data_structures/arrays/permutations.py)
276277
* [Prefix Sum](data_structures/arrays/prefix_sum.py)
277278
* [Product Sum](data_structures/arrays/product_sum.py)
279+
* [Reverse Array](data_structures/arrays/reverse_array.py)
278280
* [Rotate Array](data_structures/arrays/rotate_array.py)
281+
* [Set Matrix Zeroes](data_structures/arrays/set_matrix_zeroes.py)
279282
* [Sparse Table](data_structures/arrays/sparse_table.py)
280283
* [Sudoku Solver](data_structures/arrays/sudoku_solver.py)
281284
* Binary Tree
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
def merge_sorted_arrays(nums1: list[int], nums2: list[int]) -> list[int]:
2+
"""
3+
Merge two sorted arrays into one sorted array.
4+
5+
Args:
6+
nums1: The first sorted array.
7+
nums2: The second sorted array.
8+
9+
Returns:
10+
A single merged and sorted array.
11+
12+
Examples:
13+
>>> merge_sorted_arrays([1, 3, 5], [2, 4, 6])
14+
[1, 2, 3, 4, 5, 6]
15+
16+
>>> merge_sorted_arrays([1, 2], [])
17+
[1, 2]
18+
19+
>>> merge_sorted_arrays([], [3, 4])
20+
[3, 4]
21+
22+
>>> merge_sorted_arrays([], [])
23+
[]
24+
25+
>>> merge_sorted_arrays([0, 0], [0, 0])
26+
[0, 0, 0, 0]
27+
28+
>>> merge_sorted_arrays([-5, -3, -1], [-2, -2])
29+
[-5, -3, -2, -2, -1]
30+
31+
>>> merge_sorted_arrays(range(5), range(5))
32+
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4]
33+
34+
>>> merge_sorted_arrays([1, -1], [])
35+
Traceback (most recent call last):
36+
...
37+
ValueError: nums = [1, -1] is not sorted
38+
39+
>>> merge_sorted_arrays([], [1, -1])
40+
Traceback (most recent call last):
41+
...
42+
ValueError: nums = [1, -1] is not sorted
43+
"""
44+
for nums in (nums1, nums2):
45+
if list(nums) != sorted(nums):
46+
msg = f"{nums = } is not sorted"
47+
raise ValueError(msg)
48+
# If one array is empty, simply return the other.
49+
if not nums1:
50+
return nums2
51+
if not nums2:
52+
return nums1
53+
54+
# Two-pointer approach to merge both sorted arrays.
55+
i, j = 0, 0
56+
merged = []
57+
58+
while i < len(nums1) and j < len(nums2):
59+
if nums1[i] <= nums2[j]:
60+
merged.append(nums1[i])
61+
i += 1
62+
else:
63+
merged.append(nums2[j])
64+
j += 1
65+
66+
# Append remaining elements if any.
67+
merged.extend(nums1[i:])
68+
merged.extend(nums2[j:])
69+
70+
return merged
71+
72+
73+
if __name__ == "__main__":
74+
import doctest
75+
76+
doctest.testmod()
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):

0 commit comments

Comments
 (0)