#pragma once
#include <stdio.h>
/*需要经常用到的 2 个函数,封装成为接口直接调用*/
//1. 交换函数
void Swap(int array[], int i, int j) {
int t = array[i];
array[i] = array[j];
array[j] = t;
}
//2. 打印函数
void PrintArray(int array[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", array[i]);
}
printf("\n");
}
时间、空间复杂度、稳定性一览
排序方法 | 时间复杂度(最坏) | 时间复杂度(平均) | 时间复杂度(最好) | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
插入 | O(n^2) |
O(n^2) |
O(n) |
O(1) |
稳定 |
希尔 | O(n^2) |
O(n^1.3~1.4) |
O(n) |
O(1) |
不稳定 |
选择 | O(n^2) |
O(n^2) |
O(n^2) |
O(1) |
不稳定 |
堆 | O(n*log(n)) |
O(n*log(n)) |
O(n*log(n)) |
O(1) |
不稳定 |
冒泡 | O(n^2) |
O(n^2) |
O(n) |
O(1) |
稳定 |
快速 | O(n^2) |
O(n*log(n)) |
O(n*log(n)) |
最好O(logN) 平均O(logN) 最坏O(N) |
不稳定 |
归并 | O(n*log(n)) |
O(n*log(n)) |
O(n*log(n)) |
O(N) |
稳定 |
/*
时间复杂度:
最快:O(n) 已经有序
平均:O(n^2)
最慢:O(n^2) 逆序
【数据越有序,速度越快】
空间复杂度: O(1)
稳定性:稳定
*/
/*标准版*/
void InsertSort(int array[], int size) {
for (int i = 0; i < size; i++) {
int key = array[i];
int j;
// [i - 1, 0]
for (j = i - 1; j >= 0; j--) {
if (array[j] <= key) {
break;
}
array[j + 1] = array[j];
}
array[j + 1] = key;
}
}
/*讲解版*/
void InsertSort2(int array[], int size) {
for (int i = 0; i < size; i++) {
// array[0, i - 1] 部分是有序的
// 要处理的这个数是 array[i]
// array[i + 1, size - 1] 部分是无序的
// 我们需要在有序部分 [0, i - 1] 给 array[i] 找到合适的位置
int pos = i;
for (int j = 0; j <= i - 1; j++) {
// array[j] 比 array[i] 小,说明我们还没找到合适的位置
// array[j] 和 array[i] 相等,为了保证稳定性,我们继续前进
if (array[j] > array[i]) {
pos = j;
break;
}
}
// pos 就是应该放 array[i] 的位置
// 类似顺序表的插入,把 array[i] 插入到 array[0, i - 1] 所在的顺序表的 pos 下标
if (pos != i) {
int key = array[i];
// 避免数据被覆盖,我们倒着搬数据
// k 代表的是 数据应该被放到的空间的下标
for (int k = i; k > pos; k--) {
array[k] = array[k - 1];
}
array[pos] = key;
}
}
}
动图演示
代码实现
/*
希尔排序 -- 插入排序进化版
时间复杂度:
最好: O(n)
平均:O(n^1.3-1.4)
最坏: O(n^2)
空间复杂度: O(1)
稳定性:不稳定
*/
void InsertSortWithGap(int array[], int size, int gap) {
for (int i = 0; i < size; i++) {
int key = array[i];
int j;
for (j = i - gap; j >= 0; j -= gap) {
if (array[j] <= key) {
break;
}
array[j + gap] = array[j];
}
array[j + gap] = key;
}
}
void ShellSort(int array[], int size) {
int gap = size;
while (1) {
gap = gap / 3 + 1;
// gap = gap / 2;
InsertSortWithGap(array, size, gap);
if (gap == 1) {
return;
}
}
}
/*
【直接选择】
时间复杂度: O(n^2) 数据不敏感
空间复杂度: O(1)
稳定性:不稳定
*/
/*标准版*/
void SelectSort(int array[], int size) {
for (int i = 0; i < size; i++) {
int maxIdx = 0;
for (int j = 0; j < size - i; j++) {
if (array[j] > array[maxIdx]) {
maxIdx = j;
}
}
Swap(array, maxIdx, size - 1 - i);
}
}
/*讲解版*/
void SelectSort2(int array[], int size) {
for (int i = 0; i < size; i++) {
int record = 0; // 假设最大的数放在 array[0]
// [size - i, size - 1] 有序的
// [0, size - 1 - i] 无序的
// 在无序的部分,找到最大数的下标
for (int j = 0; j <= size - 1 - i; j++) {
if (array[j] > array[record]) {
record = j;
}
}
// 整个无序部分都找完了,已经确定最大的
// 把最大的数和无序部分的最后一个数进行交换
Swap(array, record, size - 1 - i);
}
}
动图演示
代码实现
/*
堆排序:
时间复杂度:
O(n * log(n)) 不敏感
空间复杂度 O(1)
稳定性:不稳定
*/
void Heapify(int array[], int size, int rIdx) {
while (1) {
int leftIdx = 2 * rIdx + 1;
int rightIdx = 2 * rIdx + 2;
if (leftIdx >= size) {
return;
}
int maxIdx;
if (rightIdx >= size) {
maxIdx = leftIdx;
}
else if (array[leftIdx] <= array[rightIdx]) {
maxIdx = leftIdx;
}
else {
maxIdx = rightIdx;
}
if (array[maxIdx] >= array[rIdx]) {
return;
}
Swap(array, maxIdx, rIdx);
rIdx = maxIdx;
}
}
void CreateHeap(int array[], int size) {
for (int i = (size - 1 - 1) / 2; i >= 0; i--) {
Heapify(array, size, i);
}
}
void HeapSort(int array[], int size) {
CreateHeap(array, size);
for (int i = 0; i < size; i++) {
Swap(array, 0, size - 1 - i);
Heapify(array, size - 1 - i, 0);
}
}
/*
时间复杂度:
最好:O(n)
平均 O(n^2)
最坏 O(n^2)
空间复杂度: O(1)
稳定性:稳定
*/
/*标准版*/
void BubbleSort(int array[], int size) {
for (int i = 0; i < size; i++) {
int isSorted = 1;
for (int j = 0; j < size - 1 - i; j++) {
if (array[j] > array[j + 1]) {
Swap(array, j, j + 1);
isSorted = 0; //标记
}
}
//优化逻辑:如果一次交换都没有发生,则说明已经有序
if (isSorted == 1) {
break;
}
}
}
/*讲解版*/
void BubbleSort2(int array[], int size) {
for (int i = 0; i < size; i++) {
int isSorted = 1;
// 每次冒泡,把一个最大的数挤到无序部分的最后去
// 有序部分 [size - i, size - 1]
// 无序部分 [0, size - 1 - i]
// 遍历整个无序区间,挨个挤
for (int j = 0; j < size - 1 - i; j++) {
if (array[j] > array[j + 1]) {
Swap(array, j, j + 1);
isSorted = 0;
}
}
// 如果遍历整个无序区间期间,一次交换都没发生,说明
// 其实无序区间是有序的
if (isSorted == 1) {
return;
}
}
}
/*
时间复杂度:
最好 O(n * log(n))
平均 O(n * log(n))
最坏 O(n^2)
空间复杂度:
最好 O(log(n))
平均 O(log(n))
最坏 O(n)
稳定性:不稳定
*/
// 需要排序的区间是 [low, high]
void __QuickSort(int array[], int low, int high) {
// 直到
// 区间长度为 0
if (low > high) {
return;
}
// 或者区间长度为 1,表示区间内的数已经有序
if (low == high) {
return;
}
// 1. 找基准值,选最左边,基准值的下标是 low
// 2. 遍历整个区间,把小的放左,大的放右,返回基准值所在下标
int pivotIdx = Partition_3(array, low, high);
// 3. 区间被分成三部分
// [low, pivotIdx - 1] 小
// [pivotIdx, pivotIdx] 有序
// [pivotIdx + 1, high] 大
// 分治算法,分别处理所有两个小区间
__QuickSort(array, low, pivotIdx - 1);
__QuickSort(array, pivotIdx + 1, high);
}
void QuickSort(int array[], int size) {
__QuickSort(array, 0, size - 1);
}
// Hover 法
// 对数组中 [low, high] 的区间做分组
// 基准值是 array[low]
int Partition_1(int array[], int low, int high) {
int begin = low; // 从基准值位置开始,而不是 low + 1
int end = high;
int pivot = array[low];
// (begin, end) 的区间是没有被比较过的数据
while (begin < end) {
// 重点: 如果基准值在左边,需要从右边开始走
while (begin < end && array[end] >= pivot) {
end--;
}
// array[end] 比基准值小了
while (begin < end && array[begin] <= pivot) {
begin++;
}
// array[begin] 比基准值大了
// 交换 begin 和 end 下标处的数据
Swap(array, begin, end);
}
// low 基准值
// [low + 1, begin] 比基准值小
// [begin + 1, high] 比基准值大
// 把基准值和比它小的最后一个数交换
Swap(array, low, begin);
return begin;
}
// 挖坑 法
// 对数组中 [low, high] 的区间做分组
// 基准值是 array[low]
int Partition_2(int array[], int low, int high) {
int begin = low; // 从基准值位置开始,而不是 low + 1
int end = high;
int pivot = array[low]; // begin 是坑的下标
// (begin, end) 的区间是没有被比较过的数据
while (begin < end) {
// 重点: 如果基准值在左边,需要从右边开始走
while (begin < end && array[end] >= pivot) {
end--;
}
// array[end] 比基准值小了
// 坑的下标是 begin
array[begin] = array[end];
// end 是坑的下标
while (begin < end && array[begin] <= pivot) {
begin++;
}
// array[begin] 比基准值大了
// 坑的下标是 end
array[end] = array[begin];
// begin 变成坑的下标
}
// low 基准值
// [low + 1, begin] 比基准值小
// [begin + 1, high] 比基准值大
// 把基准值和比它小的最后一个数交换
array[begin] = pivot;
return begin;
}
// 前后指针(下标)法
// 对数组中 [low, high] 的区间做分组
// 基准值是 array[low]
int Partition_3(int array[], int low, int high) {
int d = low + 1;
int i = low + 1;
int pivot = array[low];
while (i <= high) {
if (array[i] < pivot) {
Swap(array, d, i);
d++;
}
i++;
}
Swap(array, d - 1, low);
return d - 1;
}
#include <stack> //C++版
void QuickSortNoR(int array[], int size) {
std::stack<int> stack;
stack.push(0); // low
stack.push(size - 1); // high
while (!stack.empty()) {
int high = stack.top();
stack.pop();
int low = stack.top();
stack.pop();
if (low >= high) {
continue;
}
int pivotIdx = Partition_2(array, low, high);
// [low, pivotIdx - 1]
stack.push(low);
stack.push(pivotIdx - 1);
// [pivotIdx + 1, high]
stack.push(pivotIdx + 1);
stack.push(high);
}
}
/*
时间复杂度: O(n * log(n))
空间复杂度: O(n)
稳定性: 稳定
归并排序是 外部排序 的【首选】
*/
void Merge(int array[], int left, int mid, int right, int extra[]) {
int i = left; // 左边区间的下标
int j = mid; // 右边区间的下标
int k = left; // extra 的下标
while (i < mid && j < right) {
if (array[i] <= array[j]) {
extra[k++] = array[i++];
}
else {
extra[k++] = array[j++];
}
}
while (i < mid) {
extra[k++] = array[i++];
}
while (j < right) {
extra[k++] = array[j++];
}
// 把 extra 的数据移回来
for (int x = left; x < right; x++) {
array[x] = extra[x];
}
}
// [left, right)
void __MergeSort(int array[], int left, int right, int extra[]) {
// 直到
// size == 1 [3, 4)
if (right == left + 1) {
return;
}
// size == 0
if (right <= left) {
return;
}
// 1. 把数组平均分成两部分
int mid = left + (right - left) / 2;
// [left, mid)
// [mid, right)
// 2. 分治算法,排序左右两部分
__MergeSort(array, left, mid, extra);
__MergeSort(array, mid, right, extra);
// 3. 合并两个有序数组
// [left, mid) [mid, right)
Merge(array, left, mid, right, extra);
}
void MergeSort(int array[], int size) {
int *extra = (int *)malloc(sizeof(int)* size);
__MergeSort(array, 0, size, extra);
free(extra);
}
/*
时间复杂度: O(n * log(n))
空间复杂度:
时间复杂度:
最好 O(d(n + rd))
平均 O(d(n + r))
最坏 O(d(n + r))
空间复杂度:
O(n + rd)
稳定性: 稳定
【r】:关键字的基数
【d】:关键字的长度
【n】:关键字的个数
*/
void RadixSort(int arr[],int size){
int n;
for(int i = 1;i <= 100;i *= 10){
int tmp[20][10]={
0};
//20行,10列,数组每一列代表0~9位数,20行表示存放的个数上限
for(int j = 0;j < size;j++){
n = (data[j] / i) % 10;
tmp[j][n] = data[j];
}
int k = 0;
for(int p = 0;p < 10;p++)
for(int q = 0;q < size;q++){
if(tmp[q][p] != 0)
data[k++] = tmp[q][p];
}
}
}
}
测试代码
void Test() {
int array[] = {
3, 9, 1, 4, 7, 5, 2, 8, 0, 10, 9 };
int size = sizeof(array) / sizeof(int);
PrintArray(array, size);
xxx_Sort(array, size);
PrintArray(array, size);
}