Skip to content

Commit 5a4d2e2

Browse files
committed
sorts: rebase quick_sort_3_partition generic change onto upstream rewrite
1 parent ced91cd commit 5a4d2e2

1 file changed

Lines changed: 140 additions & 15 deletions

File tree

‎sorts/quick_sort_3_partition.py‎

Lines changed: 140 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from random import randrange
12
from typing import Any, Protocol
23

34

@@ -9,8 +10,8 @@ def quick_sort_3partition[T: Comparable](
910
sorting: list[T], left: int, right: int
1011
) -> None:
1112
""" "
12-
Python implementation of quick sort algorithm with 3-way partition.
13-
The idea of 3-way quick sort is based on "Dutch National Flag algorithm".
13+
Python implementation of the quicksort algorithm with 3-way partition.
14+
The idea of 3-way quicksort is based on "Dutch National Flag algorithm".
1415
1516
:param sorting: sort list
1617
:param left: left endpoint of sorting
@@ -30,10 +31,6 @@ def quick_sort_3partition[T: Comparable](
3031
>>> quick_sort_3partition(array3, 0, 0)
3132
>>> array3
3233
[]
33-
>>> array4 = ["pear", "apple", "fig"]
34-
>>> quick_sort_3partition(array4, 0, 2)
35-
>>> array4
36-
['apple', 'fig', 'pear']
3734
"""
3835
if right <= left:
3936
return
@@ -58,7 +55,7 @@ def quick_sort_lomuto_partition[T: Comparable](
5855
sorting: list[T], left: int, right: int
5956
) -> None:
6057
"""
61-
A pure Python implementation of quick sort algorithm(in-place)
58+
A pure Python implementation of the quicksort algorithm(in-place)
6259
with Lomuto partition scheme:
6360
https://en.wikipedia.org/wiki/Quicksort#Lomuto_partition_scheme
6461
@@ -80,10 +77,6 @@ def quick_sort_lomuto_partition[T: Comparable](
8077
>>> quick_sort_lomuto_partition(nums3, 0, 3)
8178
>>> nums3
8279
[-4, -2, 0, 5]
83-
>>> nums4 = ["pear", "apple", "fig"]
84-
>>> quick_sort_lomuto_partition(nums4, 0, 2)
85-
>>> nums4
86-
['apple', 'fig', 'pear']
8780
"""
8881
if left < right:
8982
pivot_index = lomuto_partition(sorting, left, right)
@@ -96,8 +89,6 @@ def lomuto_partition[T: Comparable](sorting: list[T], left: int, right: int) ->
9689
Example:
9790
>>> lomuto_partition([1,5,7,6], 0, 3)
9891
2
99-
>>> lomuto_partition(["b", "a"], 0, 1)
100-
0
10192
"""
10293
pivot = sorting[right]
10394
store_index = left
@@ -109,6 +100,142 @@ def lomuto_partition[T: Comparable](sorting: list[T], left: int, right: int) ->
109100
return store_index
110101

111102

103+
def hoare_partition_by_value[T: Comparable](
104+
array: list[T], pivot_value: T, start: int = 0, end: int | None = None
105+
) -> int:
106+
"""
107+
Returns the starting index of the right subarray, which contains the
108+
elements greater than or equal to `pivot_value`
109+
110+
>>> list_unsorted = [7, 3, 5, 4, 1, 8, 6]
111+
>>> array = list_unsorted.copy()
112+
>>> hoare_partition_by_value(array, 5)
113+
3
114+
>>> array
115+
[1, 3, 4, 5, 7, 8, 6]
116+
117+
Edge cases:
118+
>>> hoare_partition_by_value(list_unsorted.copy(), 0)
119+
0
120+
>>> hoare_partition_by_value(list_unsorted.copy(), 1)
121+
0
122+
>>> hoare_partition_by_value(list_unsorted.copy(), 2)
123+
1
124+
>>> hoare_partition_by_value(list_unsorted.copy(), 8)
125+
6
126+
>>> hoare_partition_by_value(list_unsorted.copy(), 9)
127+
7
128+
129+
"""
130+
if end is None:
131+
end = len(array) - 1
132+
133+
left = start
134+
right = end
135+
136+
while True:
137+
"""
138+
In an intermediate iteration, state could look like this:
139+
140+
lllluuuuuuuuuurrrrr
141+
^ ^
142+
| |
143+
left right
144+
145+
Where the middle values are unknown (u), since they are not yet traversed.
146+
`left-1` points to the end of the left subarray.
147+
`right+1` points to the start of the right subarray.
148+
"""
149+
150+
while array[left] < pivot_value:
151+
left += 1
152+
if left > end:
153+
# Right subarray is empty.
154+
# Signal it by returning an index out of bounds.
155+
return end + 1
156+
while array[right] >= pivot_value:
157+
right -= 1
158+
if right < start:
159+
# Left subarray is empty
160+
return start
161+
162+
if left > right:
163+
break
164+
165+
# Invariants:
166+
assert all(i < pivot_value for i in array[start:left])
167+
assert all(i >= pivot_value for i in array[right + 1 : end])
168+
"""
169+
llllllruuuuulrrrrrr
170+
^ ^
171+
| |
172+
left right
173+
"""
174+
175+
# Swap
176+
array[left], array[right] = array[right], array[left]
177+
178+
left += 1
179+
right -= 1
180+
181+
return right + 1
182+
183+
184+
def hoare_partition_by_pivot[T: Comparable](
185+
array: list[T], pivot_index: int, start=0, end: int | None = None
186+
) -> int:
187+
"""
188+
Returns the new pivot index after partitioning
189+
190+
>>> array = [7, 3, 5, 4, 1, 8, 6]
191+
>>> array[3]
192+
4
193+
>>> hoare_partition_by_pivot(array, 3)
194+
2
195+
>>> array
196+
[1, 3, 4, 6, 7, 8, 5]
197+
"""
198+
if end is None:
199+
end = len(array) - 1
200+
201+
def swap(i1, i2):
202+
array[i1], array[i2] = array[i2], array[i1]
203+
204+
pivot_value = array[pivot_index]
205+
swap(pivot_index, end)
206+
greater_or_equal = hoare_partition_by_value(
207+
array, pivot_value, start=start, end=end - 1
208+
)
209+
swap(end, greater_or_equal)
210+
return greater_or_equal
211+
212+
213+
def quicksort_hoare[T: Comparable](
214+
array: list[T], start: int = 0, end: int | None = None
215+
) -> None:
216+
"""
217+
Quicksort using the Hoare partition scheme:
218+
- https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme
219+
- The Art of Computer Programming, Volume 3: Sorting and Searching
220+
221+
>>> array = [2, 2, 8, 0, 3, 7, 2, 1, 8, 8]
222+
>>> quicksort_hoare(array)
223+
>>> array
224+
[0, 1, 2, 2, 2, 3, 7, 8, 8, 8]
225+
"""
226+
if end is None:
227+
end = len(array) - 1
228+
229+
if end + 1 - start <= 1:
230+
return
231+
232+
pivot_index_final = hoare_partition_by_pivot(
233+
array, randrange(start, end), start, end
234+
)
235+
quicksort_hoare(array, start, pivot_index_final - 1)
236+
quicksort_hoare(array, pivot_index_final + 1, end)
237+
238+
112239
def three_way_radix_quicksort[T: Comparable](sorting: list[T]) -> list[T]:
113240
"""
114241
Three-way radix quicksort:
@@ -124,8 +251,6 @@ def three_way_radix_quicksort[T: Comparable](sorting: list[T]) -> list[T]:
124251
[-5, -2, -2, 0, 1, 1]
125252
>>> three_way_radix_quicksort([1, 2, 5, 1, 2, 0, 0, 5, 2, -1])
126253
[-1, 0, 0, 1, 1, 2, 2, 2, 5, 5]
127-
>>> three_way_radix_quicksort(["pear", "apple", "fig"])
128-
['apple', 'fig', 'pear']
129254
"""
130255
if len(sorting) <= 1:
131256
return sorting

0 commit comments

Comments
 (0)