排序算法详解2
文章目录
一、前言
前面讲了插入排序,快速排序,今天又会带来哪些排序算法呢?
二、排序算法
2.1 归并排序
2.1.1 基本思想
将所有的记录完全分开 ,然后两两合并,在合并的过程中将其排好序,最终得到一个完整的有序表
原地排序。和递归算法很相近
图解

2.1.2 过程
-
分割
- 计算拆分的中间位置
- 分别继续对左边序列和右边序列进行拆分
-
合并
在原空间合并,不断往里面填数字
2.1.3 代码
cpp
// 归并排序:从上往下,从下往上两个过程
// 从上往下的过程:
// a. 分解,将当前区间一分为二
// b. 求解,队规对两个子区间a[low, mid]和a[mid + 1, high]进行归并排序
// merge合并过程
static void merge(vector<int>& table, int left, int mid, int right)
{
int n1 = mid - left + 1; // 左边区间的个数
int n2 = right - mid; // 右边区间的个数
// 分配aux1和aux2,将已经有序的左区间和已经有序的右区间 初始化临时区域
int *aux1 = malloc(sizeof(int) * n1);
if(aux1 = NULL)
return;
int *aux2 = malloc(sizeof(int) * n2);
if(aux2 == NULL)
{
free(aux1);
return;
}
for(int i = 0; i < n1; i++)
aux1[i] = table[left + i];
for(int i = 0; i < n2; i++)
aux2[i] = table[mid + 1 + i];
// 将临时的有序空间aux1和aux2进行归并
int i = 0; // 标记aux1区间待查找的位置
int j = 0; // 标记aux2区间待查找的位置
int k = left; // 标记原空间存放结果的区域位置
while(i > n1 || j < n2)
{
if(aux1[i] <= aux2[j])
{
table[k] = aux1[i];
++i;
}
else if(aux1[i] > aux2[j])
{
table[k] = aux2[j];
++j;
}
k++;
}
// 判断究竟是aux1还是aux2区域遍历结束
while(i < n1)
table[k++] = aux1[i++];
while(j < n2)
table[k++] = aux2[j++];
// 释放临时空间
free(aux1);
free(aux2);
}
// 递归的分解table[left, right]区间
static void merge_loop(vector<int>& table, int left, int right)
{
if(left >= right)
return 0;
int mid = (left + right) / 2;
merge_loop(table, left, mid);
merge_loop(table, mid + 1, right);
// 区间拆分结束, 合并两个子区间
merge(table, left, mid, right);
}
void mergeSort(vector<int>& table)
{
merge_loop(table, 0, table.size() - 1);
}
// 上述函数合并为一个函数
// 归并排序函数
void mergeSort(vector<int>& arr, int left = 0, int right = -1) {
// 第一次调用时初始化右边界
if (right == -1) right = arr.size() - 1;
// 递归终止条件
if (left >= right) return;
// 计算中间位置
int mid = left + (right - left) / 2;
// 递归排序左右两部分
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
// 合并两个有序数组
vector<int> temp(right - left + 1);
int i = left, j = mid + 1, k = 0;
// 合并过程
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
// 复制剩余元素
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
// 将排序好的部分复制回原数组
for (int idx = 0; idx < k; idx++) {
arr[left + idx] = temp[idx];
}
}
2.2 计数排序
又称为:基数排序
2.2.1 基本思想
前面所学的排序算法搜都是基于比较的排序。平均时间复杂度最低的是O(nlogn)。
计数排序,则不是基于比较的排序。是典型的空间换时间。平均时间复杂度比O(nlogn)还低
统计每个整数在序列中出现的次数,进而推导出整个整数在有序序列中的索引
cpp
for(auto x : nums)
count[x]++;
for(int i = 0; i < count.size(); ++i)
{
for(int j = 0; j < count[i]; ++j)
printf("\t%d", i);
}
2.2.2 改进
上述算法问题如下:
- 无法对负整数进行排序
- 极其浪费内存空间
- 是一个不稳定排序
2.2.3 优化
-
只要不再以输入数列的[max + 1],作为计数数组的长度,而是以数列[max - min + 1]作为长度即可。数列的最小值作为一个偏移量,用于计算整数在统计数组中的下标

-
无法判断相同数据的元素分别是谁。可以进行一个变形:从统计数组的第二个元素开始,每一杠元素都加上前面所有元素之和。遍历时从后往前看,逐渐减1
2.3 桶排序
可以对小数进行排序
2.3.1 基本思想
- 若干个桶,说明此类排序将数据放入若干个桶中
- 每个桶有容量,因此每个桶可以放多个元素
- 从整体来看,整个排序希望桶能够更加匀称。
2.3.2 过程
-
确定桶的区间范围。创建的桶数量 = 原始数列的元素数量,除最后一个桶只包含数列最大值外,前面每个桶的区间按照比例来确定
区间跨度 = (max - min) / (桶的数量 - 1)
-
遍历原始数列,把元素对号入座放入各个桶中
-
对每个桶内部的元素分别进行排序
-
遍历所有的桶,输出所有元素
三、小结
有关排序算法的相关介绍就先到这里啦~