1 -> 交换排序
基本思想:所谓交换,就是根据序列中两个记录的键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
1.1 -> 冒泡排序
冒泡排序的特性总结:
- 冒泡序列是一种非常容易理解的排序
- 时间复杂度:
- 空间复杂度:
- 稳定性:稳定
1.1.1 -> 代码实现
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<stdbool.h> // 打印 void PrintArray(int* a, int n) { for (int i = 0; i < n; i++) printf("%d ", a[i]); printf("\n"); } // 冒泡排序 void BubbleSort(int* a, int n) { for (int j = 0; j < n; ++j) { bool exchange = false; for (int i = 1; i < n - j; i++) { if (a[i - 1] > a[i]) { int tmp = a[i]; a[i] = a[i - 1]; a[i - 1] = tmp; exchange = true; } } if (exchange == false) { break; } } }
1.2 -> 快速排序
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
1.2.1 -> hoare版本
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<stdbool.h> // 交换 void Swap(int* x, int* y) { int tmp = *x; *x = *y; *y = tmp; } // 打印 void PrintArray(int* a, int n) { for (int i = 0; i < n; i++) printf("%d ", a[i]); printf("\n"); } // 快速排序hoare版本 int PartSort1(int* a, int left, int right) { int keyi = left; while (left < right) { // 右边找小 while (left < right && a[right] >= a[keyi]) { --right; } // 左边找大 while (left < right && a[left] <= a[keyi]) { ++left; } Swap(&a[left], &a[right]); } Swap(&a[keyi], &a[left]); return left; }
1.2.2 -> 挖坑法
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<stdbool.h> // 交换 void Swap(int* x, int* y) { int tmp = *x; *x = *y; *y = tmp; } // 打印 void PrintArray(int* a, int n) { for (int i = 0; i < n; i++) printf("%d ", a[i]); printf("\n"); } // 快速排序挖坑法 int PartSort2(int* a, int left, int right) { int keyi = left; while (left < right) { // 右边找小 while (left < right && a[right] >= a[keyi]) { --right; } // 左边找大 while (left < right && a[left] <= a[keyi]) { ++left; } Swap(&a[left], &a[right]); } Swap(&a[keyi], &a[left]); return left; }
1.2.3 -> 前后指针法
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<stdbool.h> // 交换 void Swap(int* x, int* y) { int tmp = *x; *x = *y; *y = tmp; } // 打印 void PrintArray(int* a, int n) { for (int i = 0; i < n; i++) printf("%d ", a[i]); printf("\n"); } // 快速排序前后指针法 int PartSort3(int* a, int left, int right) { int prev = left; int cur = left + 1; int keyi = left; while (cur <= right) { if (a[cur] < a[keyi] && ++prev != cur) { Swap(&a[prev], &a[cur]); } ++cur; } Swap(&a[prev], &a[keyi]); keyi = prev; return keyi; }
1.2.4 -> 快速排序(递归版)
void QuickSort(int* a, int left, int right) { if (left >= right) return; int keyi = PartSort3(a, left, right); // [left, keyi-1] keyi [keyi+1, right] QuickSort(a, left, keyi - 1); QuickSort(a, keyi + 1, right); }
1.2.5 -> 快速排序(非递归版)
typedef int STDataType; typedef struct Stack { STDataType* a; int top; int capacity; }ST; void STInit(ST* pst) { assert(pst); pst->a = NULL; //pst->top = -1; // top 指向栈顶数据 pst->top = 0; // top 指向栈顶数据的下一个位置 pst->capacity = 0; } void STDestroy(ST* pst) { assert(pst); free(pst->a); pst->a = NULL; pst->top = pst->capacity = 0; } void STPush(ST* pst, STDataType x) { if (pst->top == pst->capacity) { int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2; STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType)); if (tmp == NULL) { perror("realloc fail"); return; } pst->a = tmp; pst->capacity = newCapacity; } pst->a[pst->top] = x; pst->top++; } void STPop(ST* pst) { assert(pst); assert(!STEmpty(pst)); pst->top--; } STDataType STTop(ST* pst) { assert(pst); assert(!STEmpty(pst)); return pst->a[pst->top - 1]; } bool STEmpty(ST* pst) { assert(pst); /*if (pst->top == 0) { return true; } else { return false; }*/ return pst->top == 0; } int STSize(ST* pst) { assert(pst); return pst->top; } // 快速排序 非递归实现 void QuickSortNonR(int* a, int left, int right) { ST st; STInit(&st); STPush(&st, right); STPush(&st, left); while (!STEmpty(&st)) { int left = STTop(&st); STPop(&st); int right = STTop(&st); STPop(&st); //int keyi = PartSort3(a, left, right); int keyi = PartSort1(a, left, right); // [left, keyi-1] keyi [keyi+1, right] if (keyi + 1 < right) { STPush(&st, right); STPush(&st, keyi + 1); } if (left < keyi - 1) { STPush(&st, keyi - 1); STPush(&st, left); } } STDestroy(&st); }
快速排序的特性总结:
- 快速排序整体性能和使用场景较好
- 时间复杂度:
- 空间复杂度:
- 稳定性:不稳定
2 -> 归并排序
2.1 -> 归并排序
基本思想:归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
归并排序的特性总结:
- 归并排序的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决磁盘中的外排序问题
- 时间复杂度:
- 空间复杂度:
- 稳定性:稳定
2.1.1 -> 代码实现
// 归并排序递归实现 void _MergeSort(int* a, int begin, int end, int* tmp) { if (begin == end) return; int mid = (begin + end) / 2; // [begin, mid] [mid+1, end] _MergeSort(a, begin, mid, tmp); _MergeSort(a, mid + 1, end, tmp); // 归并两个区间 // ... int begin1 = begin, end1 = mid; int begin2 = mid + 1, end2 = end; int i = begin; while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[i++] = a[begin1++]; } else { tmp[i++] = a[begin2++]; } } while (begin1 <= end1) { tmp[i++] = a[begin1++]; } while (begin2 <= end2) { tmp[i++] = a[begin2++]; } memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1)); } void MergeSort(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); _MergeSort(a, 0, n - 1, tmp); free(tmp); } // 归并排序非递归实现 void MergeSortNonR(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); // 1 2 4 .... int gap = 1; while (gap < n) { int j = 0; for (int i = 0; i < n; i += 2 * gap) { // 每组的合并数据 int begin1 = i, end1 = i + gap - 1; int begin2 = i + gap, end2 = i + 2 * gap - 1; printf("[%d,%d][%d,%d]\n", begin1, end1, begin2, end2); if (end1 >= n || begin2 >= n) { break; } // 修正 if (end2 >= n) { end2 = n - 1; } while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[j++] = a[begin1++]; } else { tmp[j++] = a[begin2++]; } } while (begin1 <= end1) { tmp[j++] = a[begin1++]; } while (begin2 <= end2) { tmp[j++] = a[begin2++]; } // 归并一组,拷贝一组 memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1)); } printf("\n"); //memcpy(a, tmp, sizeof(int) * n); gap *= 2; } free(tmp); }
3 -> 非比较排序
思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
操作步骤:
- 统计相同元素出现次数
- 根据统计的结果将序列回收到原来的序列中
计数排序的特性总结:
- 计数排序在数据范围集中时,效率很高,但适用范围及场景有限
- 时间复杂度:
- 空间复杂度:
- 稳定性:稳定
3.1 -> 代码实现
// 计数排序 void CountSort(int* a, int n) { int min = a[0], max = a[0]; for (int i = 0; i < n; i++) { if (a[i] < min) { min = a[i]; } if (a[i] > max) { max = a[i]; } } int range = max - min + 1; int* countA = (int*)malloc(sizeof(int) * range); memset(countA, 0, sizeof(int) * range); // 统计次数 for (int i = 0; i < n; i++) { countA[a[i] - min]++; } // 排序 int k = 0; for (int j = 0; j < range; j++) { while (countA[j]--) { a[k++] = j + min; } } }
4 -> 排序算法复杂度及稳定性分析
5 -> 排序系列代码总结
往期:
【海贼王的数据航海】排序——概念|直接插入排序|希尔排序
【海贼王的数据航海】排序——直接选择排序|堆排序
Sort.h
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<stdbool.h> // 交换 void Swap(int* x, int* y); // 打印 void PrintArray(int* a, int n); // 排序实现的接口 // 插入排序 void InsertSort(int* a, int n); // 希尔排序 void ShellSort(int* a, int n); // 选择排序 void SelectSort(int* a, int n); // 堆排序 void AdjustUp(int* a, int child); void AdjustDown(int* a, int n, int root); void HeapSort(int* a, int n); // 冒泡排序 void BubbleSort(int* a, int n); // 快速排序递归实现 // 快速排序hoare版本 int PartSort1(int* a, int left, int right); // 快速排序挖坑法 int PartSort2(int* a, int left, int right); // 快速排序前后指针法 int PartPort3(int* a, int left, int right); void QuickSort(int* a, int left, int right); // 快速排序 非递归实现 void QuickSortnonr(int* a, int left, int right); // 归并排序递归实现 void MergeSort(int* a, int n); // 归并排序非递归实现 void MergeSortnonr(int* a, int n); // 计数排序 void CountSort(int* a, int n);
Sort.c
#include "Sort.h" #include "Stack.h" // 交换 void Swap(int* x, int* y) { int tmp = *x; *x = *y; *y = tmp; } // 打印 void PrintArray(int* a, int n) { for (int i = 0; i < n; i++) printf("%d ", a[i]); printf("\n"); } // 插入排序 void InsertSort(int* a, int n) { for (int i = 0; i < n - 1; ++i) { int end = i; int tmp = a[i + 1]; while (end >= 0) { if (a[end] > tmp) { a[end + 1] = a[end]; --end; } else { break; } a[end + 1] = tmp; } } } // 希尔排序 void ShellSort(int* a, int n) { int gap = n; for (int i = 0; i < n - gap; i++) { int end = i; int tmp = a[end + gap]; while (end >= 0) { if (a[end] > tmp) { a[end + gap] = a[end]; end -= gap; } else { break; } } a[end + gap] = tmp; } } // 选择排序 void SelectSort(int* a, int n) { int begin = 0, end = n - 1; while (begin < end) { int maxi = begin, mini = begin; for (int i = begin; i <= end; i++) { if (a[i] > a[maxi]) { maxi = i; } if (a[i] < a[mini]) { mini = i; } } Swap(&a[begin], &a[mini]); // 如果maxi和begin重叠,修正一下即可 if (begin == maxi) { maxi = mini; } Swap(&a[end], &a[maxi]); ++begin; --end; } } // 堆排序 void AdjustUp(int* a, int child) { int father = (child - 1) / 2; while (child > 0) { if (a[child] > a[father]) { Swap(&a[child], &a[father]); //更新下标 child = father; father = (father - 1) / 2; } else { break;//一旦符合小堆了,就直接退出 } } } void AdjustDown(int* a, int n, int parent) { int child = parent * 2 + 1; while (child < n) { // 找出小的那个孩子 if (child + 1 < n && a[child + 1] > a[child]) { ++child; } if (a[child] > a[parent]) { Swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } // 排升序 void HeapSort(int* a, int n) { // 建大堆 for (int i = (n - 1 - 1) / 2; i >= 0; --i) { AdjustDown(a, n, i); } int end = n - 1; while (end > 0) { Swap(&a[0], &a[end]); AdjustDown(a, end, 0); --end; } } // 冒泡排序 void BubbleSort(int* a, int n) { for (int j = 0; j < n; ++j) { bool exchange = false; for (int i = 1; i < n - j; i++) { if (a[i - 1] > a[i]) { int tmp = a[i]; a[i] = a[i - 1]; a[i - 1] = tmp; exchange = true; } } if (exchange == false) { break; } } } // 快速排序递归实现 // 快速排序hoare版本 int PartSort1(int* a, int left, int right) { int keyi = left; while (left < right) { // 右边找小 while (left < right && a[right] >= a[keyi]) { --right; } // 左边找大 while (left < right && a[left] <= a[keyi]) { ++left; } Swap(&a[left], &a[right]); } Swap(&a[keyi], &a[left]); return left; } // 快速排序挖坑法 int PartSort2(int* a, int left, int right) { int keyi = left; while (left < right) { // 右边找小 while (left < right && a[right] >= a[keyi]) { --right; } // 左边找大 while (left < right && a[left] <= a[keyi]) { ++left; } Swap(&a[left], &a[right]); } Swap(&a[keyi], &a[left]); return left; } // 快速排序前后指针法 int PartSort3(int* a, int left, int right) { int prev = left; int cur = left + 1; int keyi = left; while (cur <= right) { if (a[cur] < a[keyi] && ++prev != cur) { Swap(&a[prev], &a[cur]); } ++cur; } Swap(&a[prev], &a[keyi]); keyi = prev; return keyi; } void QuickSort(int* a, int left, int right) { if (left >= right) return; int keyi = PartSort3(a, left, right); // [left, keyi-1] keyi [keyi+1, right] QuickSort(a, left, keyi - 1); QuickSort(a, keyi + 1, right); } // 快速排序 非递归实现 void QuickSortNonR(int* a, int left, int right) { ST st; STInit(&st); STPush(&st, right); STPush(&st, left); while (!STEmpty(&st)) { int left = STTop(&st); STPop(&st); int right = STTop(&st); STPop(&st); //int keyi = PartSort3(a, left, right); int keyi = PartSort1(a, left, right); // [left, keyi-1] keyi [keyi+1, right] if (keyi + 1 < right) { STPush(&st, right); STPush(&st, keyi + 1); } if (left < keyi - 1) { STPush(&st, keyi - 1); STPush(&st, left); } } STDestroy(&st); } // 归并排序递归实现 void _MergeSort(int* a, int begin, int end, int* tmp) { if (begin == end) return; int mid = (begin + end) / 2; // [begin, mid] [mid+1, end] _MergeSort(a, begin, mid, tmp); _MergeSort(a, mid + 1, end, tmp); // 归并两个区间 // ... int begin1 = begin, end1 = mid; int begin2 = mid + 1, end2 = end; int i = begin; while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[i++] = a[begin1++]; } else { tmp[i++] = a[begin2++]; } } while (begin1 <= end1) { tmp[i++] = a[begin1++]; } while (begin2 <= end2) { tmp[i++] = a[begin2++]; } memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1)); } void MergeSort(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); _MergeSort(a, 0, n - 1, tmp); free(tmp); } // 归并排序非递归实现 void MergeSortNonR(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); // 1 2 4 .... int gap = 1; while (gap < n) { int j = 0; for (int i = 0; i < n; i += 2 * gap) { // 每组的合并数据 int begin1 = i, end1 = i + gap - 1; int begin2 = i + gap, end2 = i + 2 * gap - 1; printf("[%d,%d][%d,%d]\n", begin1, end1, begin2, end2); if (end1 >= n || begin2 >= n) { break; } // 修正 if (end2 >= n) { end2 = n - 1; } while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[j++] = a[begin1++]; } else { tmp[j++] = a[begin2++]; } } while (begin1 <= end1) { tmp[j++] = a[begin1++]; } while (begin2 <= end2) { tmp[j++] = a[begin2++]; } // 归并一组,拷贝一组 memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1)); } printf("\n"); //memcpy(a, tmp, sizeof(int) * n); gap *= 2; } free(tmp); } // 计数排序 void CountSort(int* a, int n) { int min = a[0], max = a[0]; for (int i = 0; i < n; i++) { if (a[i] < min) { min = a[i]; } if (a[i] > max) { max = a[i]; } } int range = max - min + 1; int* countA = (int*)malloc(sizeof(int) * range); memset(countA, 0, sizeof(int) * range); // 统计次数 for (int i = 0; i < n; i++) { countA[a[i] - min]++; } // 排序 int k = 0; for (int j = 0; j < range; j++) { while (countA[j]--) { a[k++] = j + min; } } }
Stack.h
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<stdlib.h> #include<assert.h> #include<stdbool.h> typedef int STDataType; typedef struct Stack { STDataType* a; int top; int capacity; }ST; void STInit(ST* pst); void STDestroy(ST* pst); void STPush(ST* pst, STDataType x); void STPop(ST* pst); STDataType STTop(ST* pst); bool STEmpty(ST* pst); int STSize(ST* pst);
Stack.c
#include "Stack.h" void STInit(ST* pst) { assert(pst); pst->a = NULL; //pst->top = -1; // top 指向栈顶数据 pst->top = 0; // top 指向栈顶数据的下一个位置 pst->capacity = 0; } void STDestroy(ST* pst) { assert(pst); free(pst->a); pst->a = NULL; pst->top = pst->capacity = 0; } void STPush(ST* pst, STDataType x) { if (pst->top == pst->capacity) { int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2; STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType)); if (tmp == NULL) { perror("realloc fail"); return; } pst->a = tmp; pst->capacity = newCapacity; } pst->a[pst->top] = x; pst->top++; } void STPop(ST* pst) { assert(pst); assert(!STEmpty(pst)); pst->top--; } STDataType STTop(ST* pst) { assert(pst); assert(!STEmpty(pst)); return pst->a[pst->top - 1]; } bool STEmpty(ST* pst) { assert(pst); /*if (pst->top == 0) { return true; } else { return false; }*/ return pst->top == 0; } int STSize(ST* pst) { assert(pst); return pst->top; }
感谢各位大佬支持!!!
互三啦!!!