Skip to content

Commit 78eee61

Browse files
ClémentClément
authored andcommitted
Notes on quicksort.
1 parent 2a5766f commit 78eee61

File tree

3 files changed

+111
-94
lines changed

3 files changed

+111
-94
lines changed

source/code/projects/Sorting/Sorting/Program.cs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,30 @@
33

44
class Program
55
{
6-
static void Main(string[] args)
7-
{
8-
Demo.Run(Sorting<int>.InsertionSort);
9-
Demo.Run(Sorting<char>.InsertionSort);
10-
Demo.Run(Sorting<int>.Heapsort);
11-
Demo.Run(Sorting<char>.Heapsort);
6+
static void Main(string[] args)
7+
{
8+
Console.WriteLine("Insertion sort:");
9+
Demo.Run(Sorting<int>.InsertionSort);
10+
Demo.Run(Sorting<char>.InsertionSort);
1211

13-
Demo.Run(Sorting<int>.BubbleSort);
14-
Demo.Run(Sorting<char>.BubbleSort);
12+
Console.WriteLine("Heap sort:");
13+
Demo.Run(Sorting<int>.Heapsort);
14+
Demo.Run(Sorting<char>.Heapsort);
1515

16-
Demo.Run(Sorting<int>.ShellSort);
17-
Demo.Run(Sorting<char>.ShellSort);
16+
Console.WriteLine("Bubble sort:");
17+
Demo.Run(Sorting<int>.BubbleSort);
18+
Demo.Run(Sorting<char>.BubbleSort);
1819

19-
Demo.Run(Sorting<int>.MergeSort);
20-
Demo.Run(Sorting<char>.MergeSort);
20+
Console.WriteLine("Shell sort:");
21+
Demo.Run(Sorting<int>.ShellSort);
22+
Demo.Run(Sorting<char>.ShellSort);
2123

2224
Console.WriteLine("Quick sort:");
23-
try
24-
{
25-
Demo.Run(Sorting<int>.QuickSort);
26-
Demo.Run(Sorting<char>.QuickSort);
27-
}catch (Exception e)
28-
{
29-
Console.Write(e.Message);
30-
}
25+
Demo.Run(Sorting<int>.QuickSort);
26+
Demo.Run(Sorting<char>.QuickSort);
27+
28+
Console.WriteLine("Selection sort:");
29+
Demo.Run(Sorting<int>.SelectionSort);
30+
Demo.Run(Sorting<char>.SelectionSort);
3131
}
3232
}

source/code/projects/Sorting/Sorting/Sorting.cs

Lines changed: 60 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -138,94 +138,52 @@ public static void ShellSort(List<T> listP)
138138

139139
// Done with shellSort algorithm.
140140

141-
// Selection Sort Algorithm
142-
public static void SelectionSort(List<T> listP)
143-
{
144-
SelectionSort(listP, 0, listP.Count - 1);
145-
}
146-
147-
private static void SelectionSort(
148-
List<T> listP,
149-
int low,
150-
int high
151-
)
152-
{
153-
T current;
154-
for (int i = low; i <= high; ++i)
155-
{
156-
T min = listP[i]; // smallest element so far
157-
int min_index = i; // index of smallest
158-
159-
for (int j = i + 1; j <= high; ++j)
160-
if (listP[j].CompareTo(min) < 0)
161-
{
162-
min = listP[j];
163-
min_index = j;
164-
}
165-
166-
if (i != min_index)
167-
{
168-
current = listP[i];
169-
listP[i] = min;
170-
listP[min_index] = current;
171-
}
172-
}
173-
}
174-
175-
// End of selection sort algorithm
176-
177141
// Quick sort algorithm
178142
public static void QuickSort(
179143
List<T> listP
180144
)
181145
{
182-
QuickSort(listP, 0, listP.Count - 1, 3);
146+
QuickSort(listP, 0, listP.Count - 1);
183147
}
184148

185149
public static void QuickSort(
186150
List<T> listP,
187151
int leftP,
188-
int rightP,
189-
int stopOnP = 3
152+
int rightP
190153
)
191154
{
192-
int range = rightP - leftP + 1;
193-
// if range is larger than stopOn use insertion sort
194-
if (range >= stopOnP)
195-
{
196-
InsertionSort(listP);
197-
}
198-
else
155+
if (leftP < rightP + 1)
199156
{
200-
T pivot = medianOfThree(listP, leftP, rightP);
201-
int i = leftP;
202-
int j = rightP;
203-
while(i < j)
157+
T pivot = MedianOfThree(listP, leftP, rightP);
158+
int left = leftP;
159+
int right = rightP;
160+
while (left <= right)
204161
{
205162
// looking for value larger
206163
// than the pivot
207164
// on the left:
208-
while (listP[i].CompareTo(pivot) < 0) i++;
165+
while (listP[left].CompareTo(pivot) < 0) left++;
209166
// looking for value smaller
210-
// than the pivot
211-
// on the right.
212-
while (pivot.CompareTo(listP[j]) < 0) j--;
167+
// than or equal to the pivot
168+
// on the right, without "crossing"
169+
// left and right.
170+
while ((left <= right) && pivot.CompareTo(listP[right]) <= 0) right--;
171+
if (left < right)
172+
Swap(listP, left, right);
213173

214-
if (i < j)
215-
Swap(listP, i, j);
216174
}
217175

218-
Swap(listP, i, rightP); // Move pivot back
219-
QuickSort(listP, leftP, i - 1, stopOnP); // sort small partition
220-
QuickSort(listP, i + 1, rightP, stopOnP); // sort large partition
176+
Swap(listP, left, rightP); // Move pivot back
177+
QuickSort(listP, leftP, left - 1); // sort left sub-list
178+
QuickSort(listP, left + 1, rightP); // sort rigth sub-list
221179
}
222180
}
223181

224-
private static T medianOfThree(
225-
List<T> listP,
226-
int left,
227-
int right
228-
)
182+
private static T MedianOfThree(
183+
List<T> listP,
184+
int left,
185+
int right
186+
)
229187
{
230188
int center = (left + right) / 2;
231189
// We sort the left, center and right
@@ -243,4 +201,42 @@ int right
243201
}
244202
// End of quick Sort algorithm
245203

204+
205+
// Selection Sort Algorithm
206+
public static void SelectionSort(List<T> listP)
207+
{
208+
SelectionSort(listP, 0, listP.Count - 1);
209+
}
210+
211+
private static void SelectionSort(
212+
List<T> listP,
213+
int low,
214+
int high
215+
)
216+
{
217+
T current;
218+
for (int i = low; i <= high; ++i)
219+
{
220+
T min = listP[i]; // smallest element so far
221+
int min_index = i; // index of smallest
222+
223+
for (int j = i + 1; j <= high; ++j)
224+
if (listP[j].CompareTo(min) < 0)
225+
{
226+
min = listP[j];
227+
min_index = j;
228+
}
229+
230+
if (i != min_index)
231+
{
232+
current = listP[i];
233+
listP[i] = min;
234+
listP[min_index] = current;
235+
}
236+
}
237+
}
238+
239+
// End of selection sort algorithm
240+
241+
246242
}

source/lectures/misc/sorting.md

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,21 +197,42 @@ Playing with the gap sequence further can give a **best**, **worst** and **avera
197197
```{download="./code/projects/Sorting.zip"}
198198
!include`snippetStart="// Quick sort algorithm", snippetEnd="// End of quick Sort algorithm"` code/projects/Sorting/Sorting/Sorting.cs
199199
```
200+
201+
### Description
200202

201-
<!--
203+
At a high level, the algorithm
202204

203-
### Description
205+
- pick a "median" value (the pivot), at the middle of the list to be sorted,
206+
- organize the list so that the values to the left of the pivot are smaller than the pivot, and the values greater than or equal to the pivot are to its right,
207+
- then recursively call quicksort on the values to the left of the pivot, and on the values to the right of the pivot,
208+
- when the lists left to be sorted are of size 1, we know that quicksort is done (a list of size 1 is sorted!), the list is sorted.
204209

205-
This algorithm works as follows:
210+
In detail, this algorithm works as follows:
206211

207-
- Choose a pivot: we use the `medianOfThree` method to select an element from the list as the pivot. Other ways of choosing the pivot exist, this "median-of-three" technique is optimal when no information about the ordering of the input is known. Note that this method actually sort those three elements "as a bonus", passing by.
208-
- Partition the list: the idea here is to re-arrange the list around the pivot. After partitioning, all elements smaller than the pivot will be on its left, and all elements greater than the pivot will be on its right. The pivot is then in its correct position, and we obtain the index of the pivot.
209-
Recursively Call: Recursively apply the same process to the two partitioned sub-arrays (left and right of the pivot).
210-
Base Case: The recursion stops when there is only one element left in the sub-array, as a single element is already sorted.
212+
- Choose a pivot: we use the `medianOfThree` method to select an element from the list as the pivot. Other ways of choosing the pivot exist, this "median-of-three" technique is optimal when no information about the ordering of the input is known. Note that this method actually sorts those three elements (at the beginning, center and end of the list to be sorted), and take the median one as the `pivot`,
213+
- The `while (left <= right)` loop in `QuickSort` proceeds as follows:
214+
215+
- It first look for the smallest `left` index such that `listP[left] > pivot`,
216+
- It then look for the greatest `right` index such that `pivot <= listP[right]` and `left <= right`,
217+
- At this point, we know that those values at `left` and `right` needs to be swapped (if `left < right`, i.e., they did not cross) and we swap them.
218+
219+
- When this loop is done, we know that `left` is where the `pivot` ought to be, it's the median value of the list we are sorting.
220+
- We can then call `Quicksort` on the two sub-lists:
221+
- the one that goes from `leftP` to `left - 1` (i.e, the values to the left of the pivot),
222+
- the one that goes from `left + 1` to `rightP` (i.e, the values to the left of the pivot),
211223

224+
One subtlety is that we know that `left` is where the `pivot` value ought to be (we actually did not know where it should have been where we started, we simply made an educated guess): hence, it was swapped at the end of `medianOfThree` and back at the end of `Quicksort`.
212225

213226
### Complexity
214-
215-
To implement the "Median of Three" strategy, select the first, last, and middle elements from your current array (or array portion as the recursive calls begin). Take the median (middle) value of those three elements to use for the current pivot.
216227

217-
-->
228+
The complexity of quick sort depends on how "lucky" we were when we picked the pivot value.
229+
The **best case** is when the pivot always divides the array in two equal halves. Let $T(n)$ be the complexity of sorting a list of size $n$ using quicksort:
230+
231+
- We need to perform a linear ($O(n)$) number of comparison, to partition the values to the left and the right of the pivot, and then to sort each-sublist,
232+
- To sort each sub-list, we need to first partition them, which takes $O(n/2)$ number of comparison,
233+
- Then, sorting each sub-list takes $T(n / 2)$.
234+
- So, we get that $T(n) = O(n) + 2 \times T(n / 2)$,
235+
- Iterating this mechanism, we get that $T(n) = 2^k \times T(n/(2^k)) + k \times n$, for $n = 2^k$,
236+
- Then, we get $k = log2(n)$ and $T(n) = n \times T(1) + n \times \log(n) = O(n \times \log(n))$.
237+
238+
The **average case** is also $O(n \times \log(n))$, but if we are unlucky with our pivot (which is always at the beginning or at the end of the list), then we have to keep on sorting a sub-list that is linear $n$ (we partition our list in lists of size $n-1$ and $1$), and we get a **worst case** complexity of $O(n^2)$.

0 commit comments

Comments
 (0)