You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The algorithms we are about to study compare [as follows](https://en.wikipedia.org/wiki/Sorting_algorithm#Comparison_of_algorithms), omitting the $O(.)$, $\times$, and letting $n$ be the size of the list we have to sort:
All the algorithms above are in-place except for merge sort.
43
45
44
46
## Helper Methods
45
47
@@ -66,17 +68,21 @@ At every step, it position a `slot` on the bar and look *back*, moving the value
66
68
67
69
### Complexity
68
70
69
-
[As explained on wikipedia](https://en.wikipedia.org/wiki/Insertion_sort#Best,_worst,_and_average_cases), the simplest worst case input is an array sorted in reverse order.
70
-
With an array sorted in reverse order, every iteration of the inner loop will scan and shift the entire sorted subsection of the array (i.e., from `bar` to the beginning) before inserting the next element. This gives a quadratic running time (i.e., $O(n^2)$).
71
+
[As explained on wikipedia](https://en.wikipedia.org/wiki/Insertion_sort#Best,_worst,_and_average_cases), the simplest **worst** case input is an array sorted in reverse order.
72
+
With an array sorted in reverse order, every iteration of the inner loop will scan and shift the entire sorted subsection of the array (i.e., from `bar` to the beginning) before inserting the next element. This gives a quadratic running time (i.e., $O(n^2)$), since `bar` is linear in `n`, and we iterate twice over it.
73
+
74
+
On the flip side, if the array is already sorted, then the algorithm is linear, since the inner loop will always execute just one time, giving an overall **best** performance of $O(n)$.
75
+
76
+
But **on average**, the algorithm remains in $O(n^2)$ since it will need to go through the list twice.
71
77
72
78
## Heapsort Algorithm
73
79
74
80
### Implementation
75
81
76
-
We first define some helper methods:
82
+
We first define a helper method:
77
83
78
84
```
79
-
!include`snippetStart="// Helper methods for Heapsort", snippetEnd="// Done with helper methods for Heapsort"` code/projects/Sorting/Sorting/Sorting.cs
85
+
!include`snippetStart="// Helper method for Heapsort", snippetEnd="// Done with helper method for Heapsort"` code/projects/Sorting/Sorting/Sorting.cs
80
86
```
81
87
82
88
and then leverage the heap structure to sort:
@@ -101,6 +107,7 @@ This algorithm works in two steps:
101
107
- The second step also calls `PercDown` $n$ times, so it is overall $O(n \times \log(n))$ as well.
102
108
103
109
Hence, the complexity of heapsort is $O(n \times \log(n))$ by [the sum rule](./docs/programming_and_computer_usage/complexity#simplifications).
110
+
Note that the **average**, **worst** and **best** complexity are all the same!
104
111
105
112
## Bubble Algorithm
106
113
@@ -116,7 +123,8 @@ The nested loop accomplishes the following: "from the beginning of the list to w
116
123
117
124
### Complexity
118
125
119
-
Since both loops depends on the size of the list, $n$, the algorithm is overall $O(n^2)$: we need to perform $n$ times $n$ operations.
126
+
Since both loops depends on the size of the list, $n$, the algorithm is **on average** $O(n^2)$: we need to perform $n$ times $n$ operations.
127
+
An optimization (not presented here) that stops the inner loop when elements were not swapped allows to bring the **best** case performance of bubble sort to linear ($O(n)$).
120
128
121
129
## ShellSort Algorithm
122
130
@@ -156,15 +164,27 @@ Consider a list of size 30, we have (assuming `current.CompareTo(listP[slot - ga
156
164
… | … | … |
157
165
1 |
158
166
159
-
The important point is to understand that we generate the sequences
167
+
The important point is to understand that we generate the sequences of pairs (`slot`, `slot-gap`) as follows:
which are sequences of values we are comparing. For the gap of 11, it means we do the following:
173
+
174
+
- First, we compare the values at indices 11 and 0, and swap them if needed,
175
+
- Then, we compare the values at indices 12 and 1, and swap them if needed,
176
+
- …
177
+
- Then, we compare the values at indices 30 and 19, and swap them if needed,
178
+
- If we did swap the values previously, then we compare the values at indices 19 and 8, and swap them if needed.
179
+
164
180
After we are done going through "the $i$ gap", we know that all values $i$ indices apart are sorted.
165
181
Reducing the value of $i$ to $1$ makes it so that the whole array is sorted.
166
182
167
-
168
183
### Complexity
169
184
185
+
The complexity of shell sort depends with the "gap sequence" that is used. We use `listP.Count / 3 + 1`, `(listP.Count / 3 + 1) / 2`, `(listP.Count / 3 + 1) / 4`, …, `1`.
186
+
This sequence follows Shell's original algorithm, and it is of complexity $O(n^2)$ in the **worst case**: indeed, we may need to explore $O(n)$ gaps, each requiring $O(n)$ swaps.
187
+
If the **best case**, if the array is already mostly sorted, then we still need to explore $O(n)$ gaps, but each gap takes only $O(\log(n))$ swaps, giving a $O(n \times \log(n))$ complexity.
188
+
On **average**, the complexity depends a lot on the sequence, but can be around $O(n^{1.5})$, which is still better than quadratic!
170
189
190
+
Playing with the gap sequence further can give a **best**, **worst** and **average** performance of $O(n \times (\log(n))^2)$!
0 commit comments