// 优化后voidSelectSort(int* arr, int n) {
int begin = 0, end = n - 1;
while (begin < end) {
int mini = begin, maxi = begin;
for (int i = begin + 1; i <= end; i++) {
if (arr[i] > arr[maxi]) {
maxi = i;
}
if (arr[i] < arr[mini]) {
mini = i;
}
}
if (begin == maxi) {
maxi = mini;
}
Swap(&arr[mini], &arr[begin]);
Swap(&arr[maxi], &arr[end]);
++begin;
--end;
}
}
int _QuickSort_Pit(int* arr, int left, int right) {
int hole = left;
int key = arr[hole];
while (left < right) {
while (left < right && arr[right] > key) {
--right;
}
arr[hole] = arr[right];
hole = right;
while (left < right && arr[left] < key) {
++left;
}
arr[hole] = arr[left];
hole = left;
}
arr[hole] = key;
return hole;
}
2.3.2.3 Lomuto 前后指针(重要)
创建前后指针,从左往右找比基准值小的进行交换,使得小的都排在基准值的左边。
int _QuickSort_Lomuto(int* arr, int left, int right) {
int prev = left, cur = left + 1;
int key = left;
while (cur <= right) {
if (arr[cur] < arr[key] && ++prev != cur) {
Swap(&arr[cur], &arr[prev]);
}
++cur;
}
Swap(&arr[key], &arr[prev]);
return prev;
}
快速排序特性总结
时间复杂度:O(nlogn)
空间复杂度:O(logn)
2.3.2.4 非递归版本(重要)
非递归版本的快速排序需要借助数据结构:栈。
// 简易栈结构定义typedefint STDataType;
typedefstructStack {
STDataType* arr;
int top;
int capacity;
} ST;
voidSTInit(ST* ps) {
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
voidSTPush(ST* ps, STDataType x) {
if (ps->top == ps->capacity) {
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
if (tmp == NULL) {
perror("realloc");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
ps->arr[ps->top++] = x;
}
voidSTPop(ST* ps) {
ps->top--;
}
STDataType STTop(ST* ps) {
return ps->arr[ps->top - 1];
}
boolSTEmpty(ST* ps) {
return ps->top == 0;
}
voidQuickSortNonR(int* arr, int left, int right) {
ST st;
STInit(&st);
STPush(&st, right);
STPush(&st, left);
while (!STEmpty(&st)) {
int begin = STTop(&st);
STPop(&st);
int end = STTop(&st);
STPop(&st);
// 单趟int keyi = begin;
int prev = begin;
int cur = begin + 1;
while (cur <= end) {
if (arr[cur] < arr[keyi] && ++prev != cur) {
Swap(&arr[prev], &arr[cur]);
}
++cur;
}
Swap(&arr[keyi], &arr[prev]);
keyi = prev;
// [begin, keyi-1] keyi [keyi+1, end]if (keyi + 1 < end) {
STPush(&st, end);
STPush(&st, keyi + 1);
}
if (begin < keyi - 1) {
STPush(&st, keyi - 1);
STPush(&st, begin);
}
}
free(st.arr);
}
2.4 归并排序(重要)
归并排序算法思想:归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并递归排序核心步骤:分解 -> 排序 -> 合并。
void _MergeSort(int* arr, int left, int right, int* tmp) {
if (left >= right) {
return;
}
int mid = (right + left) / 2;
// [left,mid] [mid+1,right]
_MergeSort(arr, left, mid, tmp);
_MergeSort(arr, mid + 1, right, tmp);
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int index = begin1;
// 合并两个有序数组为一个数组while (begin1 <= end1 && begin2 <= end2) {
if (arr[begin1] < arr[begin2]) {
tmp[index++] = arr[begin1++];
} else {
tmp[index++] = arr[begin2++];
}
}
while (begin1 <= end1) {
tmp[index++] = arr[begin1++];
}
while (begin2 <= end2) {
tmp[index++] = arr[begin2++];
}
for (int i = left; i <= right; i++) {
arr[i] = tmp[i];
}
}
voidMergeSort(int* arr, int n) {
int* tmp = (int*)malloc(sizeof(int) * n);
_MergeSort(arr, 0, n - 1, tmp);
free(tmp);
}
归并排序特性总结
时间复杂度:O(nlogn)
空间复杂度:O(n)
2.5 测试代码:排序性能对比
为了直观感受不同算法的效率,我们可以编写一个简单的测试程序,生成随机数据并计时。
voidTestOP() {
srand(time(0));
constint N = 100000;
int* a1 = (int*)malloc(sizeof(int) * N);
int* a2 = (int*)malloc(sizeof(int) * N);
int* a3 = (int*)malloc(sizeof(int) * N);
int* a4 = (int*)malloc(sizeof(int) * N);
int* a5 = (int*)malloc(sizeof(int) * N);
int* a6 = (int*)malloc(sizeof(int) * N);
int* a7 = (int*)malloc(sizeof(int) * N);
for (int i = 0; i < N; ++i) {
a1[i] = rand();
a2[i] = a1[i];
a3[i] = a1[i];
a4[i] = a1[i];
a5[i] = a1[i];
a6[i] = a1[i];
a7[i] = a1[i];
}
int begin1 = clock();
InsertSort(a1, N);
int end1 = clock();
int begin2 = clock();
ShellSort(a2, N);
int end2 = clock();
int begin3 = clock();
SelectSort(a3, N);
int end3 = clock();
int begin4 = clock();
HeapSort(a4, N);
int end4 = clock();
int begin5 = clock();
QuickSort(a5, 0, N - 1);
int end5 = clock();
int begin6 = clock();
MergeSort(a6, N);
int end6 = clock();
int begin7 = clock();
BubbleSort(a7, N);
int end7 = clock();
printf("InsertSort:%d\n", end1 - begin1);
printf("ShellSort:%d\n", end2 - begin2);
printf("SelectSort:%d\n", end3 - begin3);
printf("HeapSort:%d\n", end4 - begin4);
printf("QuickSort:%d\n", end5 - begin5);
printf("MergeSort:%d\n", end6 - end6);
printf("BubbleSort:%d\n", end7 - begin7);
free(a1); free(a2); free(a3); free(a4); free(a5); free(a6); free(a7);
}
2.6 非比较排序
2.6.1 计数排序
计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
操作步骤:
统计相同元素出现次数。
根据统计的结果将序列回收到原来的序列中。
voidCountSort(int* arr, int n) {
// 找 min,maxint min = arr[0], max = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] > max) max = arr[i];
if (arr[i] < min) min = arr[i];
}
// 确定数组大小 rangeint range = max - min + 1;
int* count = (int*)malloc(sizeof(int) * range);
if (count == NULL) {
perror("malloc fail");
exit(1);
}
// 对 count 初始化memset(count, 0, sizeof(int) * range);
// 统计次数// 通过映射的方式将数组保存在 count 数组中// 映射的方式==原数组的值-minfor (int i = 0; i < n; i++) {
count[arr[i] - min]++;
}
// 将 count 数组中的数据还原到 arr 数组中int index = 0;
for (int i = 0; i < range; i++) {
while (count[i]--) {
arr[index++] = i + min;
}
}
free(count);
}