目录
一、排序算法概述
在C语言中,通常需要手写排序算法实现对数组或链表的排序,但是在C++中,标准库中的<algorithm>头文件中已经实现了基于快排的不稳定排序算法 std::sort() ,以及稳定的排序算法 std::stable_sort() 。
排序算法通常会伴随着容器使用,当然有些时候,我们也可以直接通过容器的特性来实现对元素的去重和排序,比如 map 和 unordered_map 底层的红黑树,就可以在插入数据的时候实现排序。
虽然C++标准库中已经封装有了现成排序算法了,那么我们为何还要学习排序算法呢?
对于应聘者而言,排序算法无论是笔试还是面试,都是常考点,所以我们需要掌握排序算法的算法原理与代码实现,通过学习排序算法,能够加深我们对于代码的复杂度分析,同时也能加深我们对于数据的稳定性分析。
复杂度分为时间复杂度和空间复杂度,在早期的程序中,内存往往是限制程序运行的关键因素,因此在算法设计的时候,都会尽可能在考虑代码效率的同时去考虑节约内存,从而做到时间复杂度和空间复杂度的相对平衡,但是随着硬件技术的发展,计算机的内存越来越大,已经不再是紧缺资源了,所以如今的算法设计,会尽可能地优先考虑时间复杂度,在最大化地降低时间复杂度之后,才会考虑去节约内存,甚至有些算法会考虑牺牲空间复杂度来提高效率,比如归并排序。
稳定性在排序算法中也是需要重点考虑的一个因素,稳定性是指在排序过程中,两个等值的元素它们的相对位置是否会在排序过程中改变,如果它们的位置不变,即这是稳定的排序算法,否则就是不稳定的排序算法。
排序算法经历了长远的发展历程,到如今最广为人知的排序算法有:冒泡排序、插入排序、选择排序、希尔排序、桶排序、归并排序、快速排序、计数排序、基数排序、桶排序、外排序……
从算法思维进行分类的话,可以分为以下几类:
- 暴力求解思想:冒泡排序
- 基于插入思想:插入排序、希尔排序
- 基于选择思想:选择排序、堆排序
- 基于分治思想:归并排序、快速排序
- 基于哈希思想:计数排序、基数排序
- 基于分割思想:桶排序、外排序
从算法稳定性进行分类的话,可以进行如下分类:
- 稳定:冒泡排序、插入排序、归并排序、基数排序
- 不稳定:选择排序、希尔排序、堆排序、计数排序、快速排序
- 不确定:桶排序、外排序
二、初级排序算法
初级排序算法是指冒泡排序、插入排序、选择排序。
初级排序算法时间复杂度高,很少会在项目中直接使用,但是初级排序算法的算法思想是其他排序算法的基础,我们可以做了解,以此来对比出其他排序算法的高效性。
// 冒泡排序
// 每次遍历都将最值移动到数组末端
// 时间复杂度:O(N^2)
void BubbleSort(std::vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n-1; i++) {
bool swapped = false;
for (int j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
std::swap(arr[j], arr[j+1]);
swapped = true;
}
}
if (!swapped) {
break;
}
}
}
// 插入排序
// 每次遍历都将当前元素往前进行比较,找到它适合插入的位置
// 时间复杂度和数组的原始有序度有关,为 O(N) ~ O(N^2)
void InsertSort(std::vector<int>& arr) {
int n = arr.size();
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j+1] = arr[j];
j = j - 1;
}
arr[j+1] = key;
}
}
// 选择排序
// 每次遍历会找到最小值和最大值将其放入数组的最前端和最后端
// 只是简单实现的话,可以每次遍历只选择一个最值进行交换
// 同时选择最小值和最大值进行交换,提高了效率
// 时间复杂度:O(N^2)
void SelectSort(std::vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n / 2; i++) {
int minIndex = i;
int maxIndex = i;
for (int j = i + 1; j < n - i; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
if (arr[j] > arr[maxIndex]) {
maxIndex = j;
}
}
if (minIndex != i) {
st