Summary
There are a lot of sorting algorithms, I have only done on a part of the exercises and tests, and there are a lot of tests that are not tested because of their time complexity (e.g. bogo sort) or implementation complexity (e.g. tim sort). Here are some of the tests I did:
- Comparison sorts
- binary_tree_sort
- bubble_sort
- comb_sort
- heap_sort
- insertion_sort
- merge_sort
- odd_even_sort
- quick_sort
- selection_sort
- shell_sort
- smooth_sort
- Non-comparison sorts
- bucket_sort
- counting_sort
- radix_sort
- Other
Overview
The following information is in my algorithm implementation.
Binary tree sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(nlog(n)) |
O(nlog(n)) |
O(nlog(n)) |
O(n) |
Yes |
binary_tree_sort.cc |
Dynamic display
None.
Bubble sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(n) |
O(n²) |
O(n²) |
O(1) |
Yes |
bubble_sort.cc |
Dynamic display
Bucket sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(n+k) |
O(n²) |
O(n+k) |
O(n+k) |
Yes |
bucket_sort.cc |
Dynamic display
None.
Comb sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(nlog(n)) |
O(n²) |
O(n²/(2^p)) |
O(1) |
No |
comb_sort.cc |
Dynamic display
Counting sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
- |
O(n+r) |
O(n+r) |
O(n+r) |
Yes |
counting_sort.cc |
Dynamic display
None.
Heap sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(nlog(n)) |
O(nlog(n)) |
O(nlog(n)) |
O(1) |
No |
heap_sort.cc |
Dynamic display
Insertion sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(n) |
O(n²) |
O(n²) |
O(1) |
Yes |
insertion_sort.cc |
Dynamic display
Merge sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(nlog(n)) |
O(nlog(n)) |
O(nlog(n)) |
O(n) |
Yes |
merge_sort.cc |
Dynamic display
Odd even sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(n) |
O(n²) |
O(n²) |
O(1) |
Yes |
odd_even_sort.cc |
Dynamic display
Quick sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(nlog(n)) |
O(n²) |
O(nlog(n)) |
O(log(n)) |
No |
quick_sort.cc |
Dynamic display
Radix sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(n) |
- |
O(wn) |
O(w+n) |
Tes |
radix_sort.cc |
Dynamic display
None.
Selection sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(n²) |
O(n²) |
O(n²) |
O(1) |
No |
selection_sort.cc |
Dynamic display
Shell sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(n^(7/6)) |
O(n^(4/3)) |
- |
O(1) |
No |
shell_sort.cc |
Dynamic display
Sleep sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(1) |
- |
- |
O(n) |
No |
sleep_sort.cc |
Dynamic display
None.
Smooth sort
Best time complexity |
Worst time complexity |
Average time complexity |
Memory |
Stable |
Source code |
O(n) |
O(nlog(n)) |
O(nlog(n)) |
O(1) |
No |
smooth_sort.cc |
Dynamic display
Test
I chose some of the sorting algorithms that are commonly used or work better to show their performances in various orders of magnitude, and compared them to std::sort
. (Only for integers)
- Small amount of data (In microseconds)
Sorting algorithm |
16 |
32 |
64 |
128 |
512 |
1024 |
Bubble sort |
0.718460μs |
2.323331μs |
8.742816μs |
30.240628μs |
451.183452μs |
1876.617118μs |
Insertion sort |
0.354564μs |
0.662476μs |
1.502234μs |
4.077493μs |
39.589937μs |
134.445298μs |
Selection sort |
0.615823μs |
1.726170μs |
4.404065μs |
13.939987μs |
150.167438μs |
493.693882μs |
Merge sort |
4.292098μs |
9.013405μs |
18.521335μs |
39.645921μs |
155.457916μs |
311.587638μs |
Heap sort |
0.802436μs |
1.642194μs |
4.208122μs |
10.273043μs |
48.155471μs |
102.982355μs |
Quick sort |
1.427589μs |
2.202033μs |
4.758630μs |
9.097381μs |
40.513671μs |
84.237084μs |
Shell sort |
0.690468μs |
1.091686μs |
2.957815μs |
6.680743μs |
36.912041μs |
87.633440μs |
Smooth sort |
1.045032μs |
2.649904μs |
6.475469μs |
15.134310μs |
77.033824μs |
168.044959μs |
Comb sort |
0.513186μs |
1.110347μs |
2.351323μs |
5.514413μs |
28.999652μs |
62.673958μs |
std::sort |
0.438540μs |
0.821097μs |
2.621912μs |
5.924961μs |
31.798846μs |
66.770113μs |
- Large amount of data (In milliseconds)
Sorting algorithm |
10000 |
100000 |
1000000 |
Bubble sort |
192.724614ms |
15324.636641ms |
551999.618190ms |
Insertion sort |
11.071746ms |
1199.033438ms |
140489.051698ms |
Selection sort |
39.881986ms |
3896.193842ms |
394233.806428ms |
Merge sort |
3.423321ms |
32.797505ms |
336.168389ms |
Heap sort |
1.547394ms |
17.826575ms |
282.484509ms |
Quick sort |
1.045033ms |
8.998943ms |
78.881106ms |
Shell sort |
1.200761ms |
15.585073ms |
180.192715ms |
Smooth sort |
2.136252ms |
25.397649ms |
350.425244ms |
Comb sort |
0.988116ms |
10.678459ms |
126.565752ms |
std::sort |
0.845077ms |
10.250556ms |
104.314305ms |
From the above table, we can see that (In my sorting algorithms implementation):
- When the amount of data is less than 128,
insertion_sort
is more advantageous.
- When the amount of data is between 128 and 10000,
comb_sort
is more advantageous.
- When the amount of data is greater than 10000,
quick_sort
is more advantageous.
In order to get more accurate data, I have done a further test.
Sorting algorithm |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
2048 |
4096 |
8192 |
10000 |
100000 |
1000000 |
10000000 |
100000000 |
Insertion sort |
0.401218μs |
0.690468μs |
1.558218μs |
4.198791μs |
13.090898μs |
45.300293μs |
167.597088μs |
518.410767μs |
2153.046876μs |
9295.592109μs |
- |
- |
- |
- |
- |
Comb sort |
0.606492μs |
1.250307μs |
2.836517μs |
6.494131μs |
16.561899μs |
36.277557μs |
84.600980μs |
163.528925μs |
330.892747μs |
747.338199μs |
0.901061ms |
12.528727ms |
140.981603ms |
1540.349918ms |
16413.311661ms |
Quick sort |
1.166331μs |
2.286009μs |
4.870598μs |
10.459656μs |
22.356231μs |
47.968858μs |
102.991685μs |
180.827946μs |
386.783325μs |
804.982939μs |
0.942489ms |
10.484755ms |
91.543914ms |
865.948456ms |
8535.924019ms |
std::sort |
0.485194μs |
0.951726μs |
2.799194μs |
6.596768μs |
14.947697μs |
33.711629μs |
74.971752μs |
138.485468μs |
308.657814μs |
629.482793μs |
0.817458ms |
9.687451ms |
100.566277ms |
961.007785ms |
9889.731343ms |
Now I am sure that:
- When the amount of data is less than 256,
insertion_sort
has a very good performance, even better than std::sort
.
- When the amount of data is between 256 and 10000, the performance of
comb_sort
is slightly better than quick_sort
.
- When the amount of data is between 256 and 100000, the performance of
std::sort
is the best.
- When the amount of data is greater than 100000, the performance of
std::sort
is not as good as quick_sort
.
How can I beat std::sort
? So I referred to the practice of introsort, but did not control the split depth. I call it Impro sort, and now use it to compare with quick_sort
and std::sort
.
Sorting algorithm |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
2048 |
4096 |
8192 |
10000 |
100000 |
1000000 |
10000000 |
100000000 |
Quick sort |
1.166331μs |
2.286009μs |
4.870598μs |
10.459656μs |
22.356231μs |
47.968858μs |
102.991685μs |
180.827946μs |
386.783325μs |
804.982939μs |
0.942489ms |
10.484755ms |
91.543914ms |
865.948456ms |
8535.924019ms |
Impro sort |
0.466531μs |
0.886410μs |
2.108721μs |
6.326164μs |
12.363079μs |
27.637313μs |
59.771987μs |
125.935454μs |
278.136618μs |
479.678131μs |
0.617594ms |
7.230302ms |
84.720028ms |
724.386383ms |
7360.014444ms |
std::sort |
0.485194μs |
0.951726μs |
2.799194μs |
6.596768μs |
14.947697μs |
33.711629μs |
74.971752μs |
138.485468μs |
308.657814μs |
629.482793μs |
0.817458ms |
9.687451ms |
100.566277ms |
961.007785ms |
9889.731343ms |
From the latest test can be seen, no matter in which respect, Impro sort
havs improved than Quick sort
, that's why I call it Impro sort
, which means "Improved sort". And in my test, Impro sort
achieved a comprehensive victory, even faster than std::sort
. Oh yeah! Although the results may not be so accurate, this shows that there is space for improvement in sorting algorithm, and we can do better through our efforts.