1+ from random import randrange
12from 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+
112239def 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