常用排序算法总结
目录
学习排序算法时的总结和记录
各种排序算法比较
排序 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
直接插入排序 | O(n^2) | O(1) | 稳定 |
希尔排序 | 根据增量序列的不同时间复杂度不同;可以很接近O(nlogn) | O(1) | 不稳定 |
冒泡排序 | O(n^2) | O(1) | 稳定 |
快速排序 | 期望值O(nlogn),需要考虑数组几乎有序和存在大量重复元素的情况 | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(n) | 不稳定 |
堆排序 | O(nlogn) | O(n) | 不稳定 |
直接插入排序(InsertSort)
template <typename T>
void SelectSort(T* arr, int n) {
for (int i = 0; i < n; i++) {
int idx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[idx]) {
idx = j;
}
}
swap(arr[i], arr[idx]);
}
}
希尔排序(ShellSort)
普通希尔排序
template <typename T>
void ShellSort(T* arr, int n) {
for (int gap = n / 2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
int j;
T tmp = arr[i];
for (j = i; j >= gap && arr[j - gap] > tmp; j -= gap) {
arr[j] = arr[j - gap];
}
arr[j] = tmp;
}
}
}
优化希尔排序(调整increment sequence)
template<typename T>
void ShellSort(T arr[], int n) {
int h = 1;
while (h < n / 3)
h = 3 * h + 1;
// 计算 increment sequence: 1, 4, 13, 40, 121, 364, 1093...
while (h >= 1) {
// h-sort the array
for (int i = h; i < n; i++) {
// 对 arr[i], arr[i-h], arr[i-2*h], arr[i-3*h]... 使用插入排序
T e = arr[i];
int j;
for (j = i; j >= h && e < arr[j - h]; j -= h)
arr[j] = arr[j - h];
arr[j] = e;
}
h /= 3;
}
}
冒泡排序(BubbleSot)
template <typename T>
void BubbleSot(T* arr, int n) {
for (int i = 1; i < n; i++) {
for (int j = 0; j < n - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
}
}
}
}
冒泡排序的优化
// * 用一个变量flag记录下最后一个发生交换的位置,后面没有发生交换的已经有序
// * 所以可以用这个值来作为下一次比较结束的位置
template<typename T>
void BubbleSot(T *arr, int n)
{
int flag = n;
int j, k;
for(int i = 0; i < flag; i++) {
k = flag; //保存最后一次交换的位置下标
flag = 0; //如果flag值不变则说明之后的数据有序,因此可退出程序
for(j = 0; j < k; j++) {
if(arr[j] > arr[j+1]) {
flag = j;
swap(arr[j], arr[j+1]);
}
}
}
}
快速排序(QuickSort)
普通快排
快排核心步骤就是partition操作,partition操作一般情况下就是以arr[l]元素(记为tmp)为基准将arr分为大于tmp和小于tmp两部分 , 最终将其分为 arr[l ... j] < tmp和arr[j+1 ... r] > tmp两部分
各个变量定义
-
l:arr左边界
-
r:arr有边界
-
i:准备进行判断比较的位置
-
j:
arr[l ... j] < tmp
,一开始j == l
时表示没有元素小于tmp
template <typename T>
int _Partition(T* arr, int l, int r) {
int j = l;
T tmp = arr[l];
for (int i = l + 1; i <= r; i++) {
if (arr[i] < tmp) {
swap(arr[j + 1], arr[i]);
j++;
}
}
swap(arr[j], arr[l]);
return j;
}
普通快排的缺陷
- 当数组近乎有序的时候,算法会退化成O(n^2)级别的算法
采用随机选择一个标定元素,则该排序的期望值为O(nlogn),大概率上避免退化成O(n^2);在partition一开始的位置 swap(arr[l], arr[(rand() % (r - l + 1)) + l]);
- 当存在大量重复元素的时候,算法也会退化成O(n^2)
普通快排的优化
- 将整个数组通过标定元素比较分成三个部分,小于标定,等于标定,大于标定因此如果存在大量重复元素也能高效执行算
template <typename T>
void _QuickSort3(T* arr, int l, int r) {
// 当元素少于16个时,使用插入排序作为快速排序的自排序更为高效
if (r - l <= 16) {
InsertSort(arr + l, r - l + 1);
return;
}
swap(arr[l], arr[(rand() % (r - l + 1)) + l]);
int tmp = arr[l];
int lt = l; // arr[l...lt] < tmp
int gt = r + 1; // arr[gt...r] > tmp
int i = l + 1; // arr[lt+1...i-1] == tmp
while (gt > i) {
if (arr[i] < tmp) {
swap(arr[++lt], arr[i++]);
} else if (arr[i] > tmp) {
swap(arr[--gt], arr[i]);
} else { // tmp == arr[i]
i++;
}
}
swap(arr[lt], arr[l]);
_QuickSort3(arr, l, lt - 1);
_QuickSort3(arr, gt, r);
}
template <typename T>
void QuickSort3(T* arr, int n) {
srand(time(NULL));
_QuickSort3(arr, 0, n - 1);
}
归并排序(MergeSort)
Merge操作
// 将arr[l, mid]和arr[mid+1, r]进行合并
template <typename T>
void _Merge(T* arr, int l, int mid, int r) {
T* aux = new T[r - l + 1];
for (int i = l; i <= r; i++) {
aux[i - l] = arr[i];
}
int i = l;
int j = mid + 1;
for (int k = l; k <= r; k++) {
if (i > mid) {
arr[k] = aux[j - l];
j++;
} else if (j > r) {
arr[k] = aux[i - l];
i++;
} else if (aux[i - l] < aux[j - l]) {
arr[k] = aux[i - l];
i++;
} else {
arr[k] = aux[j - l];
j++;
}
}
delete[] aux;
}
// 对于arr[l, r]进行归并排序;l,r是闭区间
template <typename T>
void _MergeSort(T* arr, int l, int r) {
// arr长度不大于15的时候使用插入排序
// 可提升算法效率
if (r - l <= 15) {
InsertSort(arr + l, r - l + 1);
return;
}
if (l >= r) {
return;
}
int mid = (r + l) / 2;
_MergeSort(arr, l, mid);
_MergeSort(arr, mid + 1, r);
_Merge1(arr, l, mid, r);
}
template <typename T>
void MergeSort(T* arr, int n) {
_MergeSort(arr, 0, n - 1);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理