Analysis of different sorting techniques
Bubble Sort
冒泡排序的算法时间复杂度是 n^2
Implementation:
// cpp
void bubble_sort(std::vector<int>& array) {
int size = array.size();
for (int i = size - 1; i > 0; --i) {
for (int j = 0; j < i; ++j) {
if (array[j] > array[j + 1]) {
int tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
}
}
}
}
Optimized Implementation:
The above function always runs O(n^2) time even if the array is sorted. It can be optimized by stopping the algorithm if inner loop didn’t cause any swap.
// cpp
// An optimized version of Bubble Sort
void bubble_sort(std::vector<int>& array) {
int size = array.size();
for (int i = size - 1; i > 0; --i) {
bool swapped = false;
for (int j = 0; j < i; ++j) {
if (array[j] > array[j + 1]) {
int tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
swapped = true;
}
}
// IF no two elements were swapped by inner loop, then break
if (!swapped) break;
}
}
Selection Sort
选择排序的时间复杂度为 O(n^2),但性能上略优于冒泡排序。
选择排序提高了冒泡排序的性能,它每遍历一次列表只交换一次数据,即进行一次遍历时找到最大(或最小)的项,完成遍历后,再把它换到正确的位置。和冒泡排序一样,第一次遍历后,最大(或最小)的项就已归位,第二次遍历次大(次小)项归位。这个过程持续进行,一共需要 n-1 次遍历来排好 n 个数,因为最后一个数必须在第 n-1 次遍历之后才能归位。
// cpp
void selection_sort(std::vector<int>& array) {
int size = array.size();
for (int i = size - 1; i > 0; --i) {
int max_idx = i;
for (int j = 0; j < i; ++j) {
if (array[j] > array[max_idx])
max_idx = j;
}
if (max_idx != i) {
int tmp = array[i];
array[i] = array[max_idx];
array[max_idx] = tmp;
}
}
}
Insertion Sort
插入排序的算法时间复杂度仍然是 n^2,但其工作原理稍有不同。它总是保持一个位置靠前的已排好的子表,然后每一个新的数据项被 “插入” 到前边的子表里,排好的子表增加一项。我们认为只含有一个数据项的列表是已经排好的。每排后面一个数据(从 1 开始到 n-1),这 个的数据会和已排好子表中的数据比较。比较时,我们把之前已经排好的列表中比这个数据大的移到它的右边。当子表数据小于当前数据,或者当前数据已经和子表的所有数据比较了时,就可以在此处插入当前数据项。
// cpp
void insertion_sort(std::vector<int>& array) {
int size = array.size();
int i, key, j;
for (i = 1; i < size; i++) {
key = array[i];
j = i - 1;
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
--j;
}
array[j + 1] = key;
}
}
Shell Sort
shell_sort
视频来源:https://www.codingeek.com/algorithms/shell-sort-algorithm-explanation-implementation-and-complexity/
void shell_sort(std::vector<int>& array) {
int size = array.size();
for (int gap = size / 2; gap > 0; gap /= 2) {
for (int i = gap; i < size; ++i) {
int key = array[i];
int j;
for (j = i; j >= gap && array[j - gap] > key; j -= gap) {
// 把较小的值往前移动,直到前面没有比它小的或取到子序列尾了
int tmp = array[j];
array[j] = array[j - gap];
array[j - gap] = tmp;
}
}
}
}
上面代码可以进行小优化:
void shell_sort(std::vector<int>& array) {
int size = array.size();
for (int gap = size / 2; gap > 0; gap /= 2) {
for (int i = gap; i < size; ++i) {
int key = array[i];
int j;
for (j = i; j >= gap && array[j - gap] > key; j -= gap)
// 把较小的值往前移动,直到前面没有比它小的或取到子序列尾了
array[j] = array[j - gap];
array[j] = key;
}
}
}
Conclusion
前面说到,选择是冒泡的一种优化,但如果序列乱序较少,用冒泡本身的优化方法是最好的,效率更高。但选择,插入,冒泡复杂度都是n^2
所以基本不在这里面选择,重点应该比较的是堆排序,快排和归并排序。