1. 堆排序
(1)算法描述
① 将初始待排序的序列构建成大堆,即为初始的无序区间;
② 将堆顶元素与最后一个元素进行交换(此时,无序区间与有序区间分别是[1,n-1]、[n]);
③ 对无序区间的序列继续建大堆,执行②,更新无序区间和有序区间;
④ 重复③,直至有序区间的元素个数为n-1个,则数据有序。
(2)代码实现
//堆排序 时间复杂度:O(n(logn)) 不稳定
//建大堆,从最后一个
void shiftDown(int* arr, int n, int parent)
{
int child = 2 * parent + 1;
while (child < n)
{
if (child + 1 < n && arr[child + 1] > arr[child])
++child;
if (arr[child] > arr[parent])
{
swap(arr, child, parent);
parent = child;
child = 2 * parent + 1;
}
else
break;
}
}
void heapSort(int* arr, int n)
{
for (int i = (n - 2) / 2; i >= 0; i--)
{
shiftDown(arr, n, i);
}
int end = n - 1;
while (end > 0)
{
swap(arr, end, 0);
shiftDown(arr, end, 0);
end--;
}
}
2. 归并排序
算法描述 (分治法)
① 把长度为n的序列,分成n/2的的两个子序列;
② 对两个子序列进行归并排序;
③ 将两个排好序的子序列,合并成一个序列,即排序完成。
(1)递归实现
//相邻子序列合并:
void merge(int* arr, int begin,int mid, int end, int* tmp)
{
//递增
int begin1 = begin;
int end1 = mid;
int begin2 = mid + 1;
int end2 = end;
//辅助空间的起始位置
int idx = begin;
//合并有序序列 稳定
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] <= arr[begin2])
tmp[idx++] = arr[begin1++];
else
tmp[idx++] = arr[begin2++];
}
//判断是否有未合并的元素
if (begin1 <= end1)
memcpy(tmp + idx, arr + begin1, sizeof(int)*(end1 - begin1 + 1));
if (begin2 <= end2)
memcpy(tmp + idx, arr + begin2, sizeof(int)*(end2 - begin2 + 1));
//合并之后的序列拷贝到原始数组的对应区间
memcpy(arr + begin, tmp + begin, sizeof(int)*(end - begin + 1));
}
//归并排序 递归 n(logn)
void _mergeSort(int* arr, int begin, int end, int* tmp)
{
if (begin >= end)
return;
int mid = begin + (end - begin) / 2;
//首先合并子序列
_mergeSort(arr, begin, mid, tmp);
_mergeSort(arr, mid + 1, end, tmp);
//合并两个有序的子序列
merge(arr, begin, mid, end, tmp);
}
void mergeSort(int* arr, int n)
{
//申请辅助空间
int* tmp = (int*)malloc(sizeof(int)*n);
_mergeSort(arr, 0, n - 1, tmp);
free(tmp);
}
(2)非递归实现
//归并排序 非递归
void mergeSortNOR(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
//子序列步长
int step = 1;
while (step < n)
{
for (int idx = 0; idx < n; idx += 2 * step)
{
//找到两个待合并的子序列区间
//[begin,mid] [mid+1,end]
int begin = idx;
int mid = idx + step - 1;
//判断是否存在第二个序列
if (mid >= n - 1)
//不存在第二个子序列,直接跳过
continue;
int end = idx + 2 * step - 1;
//判断第二个子序列是否越界
if (end >= n)
end = n - 1;
merge(arr, begin, mid, end, tmp);
}
//更新步长
step *= 2;
}
}
3. 计数排序
(1)算法描述
① 找到待排序的数组中最大值和最小值;
② 依次统计数组中每个值出现的次数,存入另一个计数数组;
③ 遍历计数数组中的元素,即排序完成。
(2)代码实现
//计数排序
void countSort(int* arr, int n)
{
//找到最大和最小值
int max, min;
min = max = arr[0];
for (int i = 1; i < n; ++i)
{
if (arr[i] > max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
//计数范围
int range = max - min + 1;
//创建一个计数数组 初始化为0
int* countArr = (int*)calloc(range, sizeof(int));
//计数
for (int i = 0; i < n; ++i)
{
countArr[arr[i] - min]++;
}
//遍历计数数组,排序
int idx = 0;
for (int i = 0; i < range; ++i)
{
while (countArr[i]--)
arr[idx++] = i + min;
}
}