// 优化后:双向选择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 非递归版本
思路
非递归版本的快速排序需要借助数据结构:栈。模拟递归调用的过程。
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);
// 单趟逻辑(Lomuto 法)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;
// 入栈范围if (keyi + 1 < end) {
STPush(&st, end);
STPush(&st, keyi + 1);
}
if (begin < keyi - 1) {
STPush(&st, keyi - 1);
STPush(&st, begin);
}
}
STDestroy(&st);
}
void _MergeSort(int* arr, int left, int right, int* tmp) {
if (left >= right) return;
int mid = (right + left) / 2;
_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) {
int 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];
}
int range = max - min + 1;
int* count = (int*)malloc(sizeof(int) * range);
if (count == NULL) {
perror("malloc fail");
exit(1);
}
memset(count, 0, sizeof(int) * range);
for (int i = 0; i < n; i++) {
count[arr[i] - min]++;
}
int index = 0;
for (int i = 0; i < range; i++) {
while (count[i]--) {
arr[index++] = i + min;
}
}
free(count);
}
// Sort.h#pragma once#include<stdio.h>#include<memory.h>#include<stdlib.h>voidInsertSort(int* arr, int n);
voidShellSort(int* arr, int n);
voidSelectSort(int* arr, int n);
voidHeapSort(int* arr, int n);
voidBubbleSort(int* arr, int n);
voidQuickSort(int* arr, int left, int right);
voidQuickSortNonR(int* arr, int left, int right);
voidMergeSort(int* arr, int n);
voidCountSort(int* arr, int n);
voidSwap(int* x, int* y);
// Sort.c#include"Sort.h"#include"Stack.h"voidInsertSort(int* arr, int n) {
for (int i = 0; i < n - 1; i++) {
int end = i;
int tmp = arr[end + 1];
while (end >= 0) {
if (arr[end] > tmp) {
arr[end + 1] = arr[end];
end--;
} else {
break;
}
}
arr[end + 1] = tmp;
}
}
voidShellSort(int* arr, int n) {
int gap = n;
while (gap > 1) {
gap = gap / 3 + 1;
for (int i = 0; i < n - gap; i++) {
int end = i;
int tmp = arr[end + gap];
while (end >= 0) {
if (arr[end] > tmp) {
arr[end + gap] = arr[end];
end -= gap;
} else {
break;
}
}
arr[end + gap] = tmp;
}
}
}
voidSwap(int* x, int* y) {
int tmp = *x;
*x = *y;
*y = tmp;
}
voidAdjustDown(int* arr, int parent, int n) {
int child = parent * 2 + 1;
while (child < n) {
if (child + 1 < n && arr[child] < arr[child + 1]) {
child++;
}
if (arr[child] > arr[parent]) {
Swap(&arr[child], &arr[parent]);
parent = child;
child = parent * 2 + 1;
} else {
break;
}
}
}
voidHeapSort(int* arr, int n) {
for (int i = (n - 1 - 1) / 2; i >= 0; i--) {
AdjustDown(arr, i, n);
}
int end = n - 1;
while (end > 0) {
Swap(&arr[0], &arr[end]);
AdjustDown(arr, 0, end);
end--;
}
}
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;
}
}
voidBubbleSort(int* arr, int n) {
int exchange = 0;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
exchange = 1;
Swap(&arr[j], &arr[j + 1]);
}
}
if (exchange == 0) break;
}
}
int _QuickSort_Hoare(int* arr, int left, int right) {
int keyi = left; left++;
while (left <= right) {
while (left <= right && arr[right] > arr[keyi]) --right;
while (left <= right && arr[left] < arr[keyi]) ++left;
if (left <= right) Swap(&arr[left++], &arr[right--]);
}
Swap(&arr[keyi], &arr[right]);
return right;
}
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;
}
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;
}
voidQuickSort(int* arr, int left, int right) {
if (left >= right) return;
int keyi = _QuickSort_Lomuto(arr, left, right);
QuickSort(arr, left, keyi - 1);
QuickSort(arr, keyi + 1, right);
}
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;
if (keyi + 1 < end) { STPush(&st, end); STPush(&st, keyi + 1); }
if (begin < keyi - 1) { STPush(&st, keyi - 1); STPush(&st, begin); }
}
STDestroy(&st);
}
void _MergeSort(int* arr, int left, int right, int* tmp) {
if (left >= right) return;
int mid = (right + left) / 2;
_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(n * sizeof(int));
_MergeSort(arr, 0, n - 1, tmp);
free(tmp);
}
voidCountSort(int* arr, int n) {
int 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];
}
int range = max - min + 1;
int* count = (int*)malloc(sizeof(int) * range);
if (count == NULL) { perror("malloc fail"); exit(1); }
memset(count, 0, sizeof(int) * range);
for (int i = 0; i < n; i++) count[arr[i] - min]++;
int index = 0;
for (int i = 0; i < range; i++) {
while (count[i]--) arr[index++] = i + min;
}
free(count);
}