// 优化后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(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(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);
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 = 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 - begin6);
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);
}