博客主页: 云曦
系列专栏:数据结构吾生也有涯,而知也无涯 感谢大家点赞 关注评论
在上期我们讲解完了普通二叉树,这期又是一期新的知识点(排序)。
- 插入排序是一种很简单的排序法,其思想就是:
- 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列
- 其次排序与之前的数据结构不同,我们实现的时候一般先实现单趟排序,再去实现多趟排序。
- 单趟的思路:
- 单趟实现:
void InsetionSort(int* arr, int n)
{
int end = i;
int tmp = arr[end + 1];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + 1] = arr[end];
}
else
{
break;
}
end--;
}
arr[end + 1] = tmp;
}
- 多趟思路:
- 在套一层循环
- 需要注意的是:end最后落到的位置是n-2的位置,n-1的话会把下标为n的数据插入进去导致越界访问
void InsetionSort(int* arr, int n)
{
int i = 0;
//end最后停留的位置是n-2的位置
//所以i要小于n-1
for (i = 0; i < n - 1; i++)
{
int end = i;
int tmp = arr[end + 1];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + 1] = arr[end];
}
else
{
break;
}
end--;
}
arr[end + 1] = tmp;
}
}
- 时间复杂度:
最坏情况(要升序,而数组是逆序):O(N^2)
最好情况(要升序,而数组是顺序有序):O(N)- 空间复杂度:O(1)
- 希尔排序是在直接插入排序的基础上变形的一种排序。
- 希尔排序分为:
- 预排序
预排序是分组排序,间隔gap的为一组,- 直接插入排序
- 首先假设gap==3,end从下标0开始,tmp记录end+gapx位置的元素
- 然后判断tmp记录的元素是否小于end位置的元素,小于就把end位置的元素往后挪到end+gap的位置上,大于就break跳出循环,叫tmp赋值给end+gap的位置上
- 这个思路就类似直接插入排序,就是把加1的位置变成了加gap
void ShellSort(int* arr, int n)
{
int gap = 3;
int end = 0;
int tmp = arr[end + gap];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
上面的过程,只是执行了一组内一次的过程,要一组都执行完的话,还要加一层循环
- 定义i从0开始小于n-gap,每次循环上来减等gap
void ShellSort(int* arr, int n)
{
int gap = 3;
int i = 0;
for (i = 0; i < n - gap; i += gap)
{
int end = 0;
int tmp = arr[end + gap];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}
- gap组执行,简单嘛,再加一层循环
循环从0开始,小于n结束
void ShellSort(int* arr, int n)
{
int gap = 3;
int j = 0;
for (j = 0; j < n; j++)
{
int i = j;
for (i = j; i < n - gap; i += gap)
{
int end = 0;
int tmp = arr[end + gap];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}
}
- 上面这部分逻辑有的地方是这样写的:
void ShellSort(int* arr, int n)
{
int gap = 3;
int i = 0;
for (i = 0; i < n - gap; ++i)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
- 那么问题来了,gap应该取多少合适呢?
答案是:把n赋值给gap,然后gap = gap / 2,除2的数可以保证最后一次gap永远是1,就是直接插入排序。
有人决定gap/2比较慢,就改成了gap/3,也是可以的,但gap/3时要注意一点:gap小于3或其他数时,会除出0来,所以要写成gap/3+1
void ShellSort(int* arr, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 2;
//gap = gap / 3 + 1;
int i = 0;
for (i = 0; i < n - gap; ++i)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}
}
void TestShellSort()
{
int arr[] = { 9,1,2,5,7,4,8,6,3,5 };
int size = sizeof(arr) / sizeof(arr[0]);
InsertSort(arr, size);
PrintArray(arr, size);
}
- gap/2的话,时间复杂度:O(log₂N)
gap/3+1的话,时间复杂度:O(log₃N)
也有些地方通过计算得出时间复杂度:O(N^1.3)- 空间复杂度:O(1)
- 直接插入排序是一个很简单的排序
- 先找出最大值和最小值的下标
void SelectSort(int* arr, int n)
{
int mini = begin;
int maxi = begin;
int i = 0;
for (i = begin+1; i < end; i++)
{
if (arr[i] > arr[maxi])
{
maxi = i;
}
if (arr[i] < arr[mini])
{
mini = i;
}
}
}
- 套上循环,左边从0开始,右边从n-1开始,形成左闭右闭
每次找到最大和最小的值和左右边上交换,然后左加1,右-1
- 需要注意的是:maxi和begin有可能在一个位置上,begin和mini先交换,有可能会导致maxi位置上的值变成其他的,这时我们要加上一个判断进行修正
void SelectSort(int* arr, int n)
{
int begin = 0;
int end = n - 1;
while (begin < end)
{
//[begin,end] 左闭右闭
int mini = begin;
int maxi = begin;
int i = 0;
for (i = begin+1; i < end; i++)
{
if (arr[i] > arr[maxi])
{
maxi = i;
}
if (arr[i] < arr[mini])
{
mini = i;
}
}
Swap(&arr[begin], &arr[mini]);
//max如果被换走,就修正一下
if (maxi == begin)
{
maxi = mini;
}
Swap(&arr[end], &arr[maxi]);
++begin;
--end;
}
}
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
堆排序在二叉树的篇章讲过了,就不在论述了,大家可以去此链接查看:
二叉树讲解堆排序的篇章
- 时间复杂度:O(N*logN)
- 空间复杂度:O(1)
- 冒泡排序就不用说了,相信大家在学习C语言的语法时,就已经学过了
- 思路也就是两两比较,小于就交换
void BubbleSort(int* arr, int n)
{
int j = 0;
for (j = 0; j < n; j++)
{
int flag = 0;
int i = 1;
for (i = 1; i < n-j; i++)
{
if (arr[i - 1] > arr[i])
{
Swap(&arr[i - 1], &arr[i]);
flag = 1;
}
}
if (flag == 0)
{
break;
}
}
}
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
int PartSort1(int* arr, int left, int right)
{
int kay = arr[left];
while (left < right)
{
//找小
while (arr[right] > kay)
{
--right;
}
//找大
while (arr[left] < kay)
{
++left;
}
Swap(&arr[right], &arr[left]);
}
Swap(&arr[left], &kay);
return left;
}
- 这样单趟就写好了,不过还没写完,与几处地方有坑
int PartSort1(int* arr, int left, int right)
{
int kay = arr[left];
while (left < right)
{
//找小
while (arr[right] >= kay)
{
--right;
}
//找大
while (arr[left] <= kay)
{
++left;
}
Swap(&arr[right], &arr[left]);
}
Swap(&arr[left], &kay);
return left;
}
int PartSort1(int* arr, int left, int right)
{
int kay = left;
while (left < right)
{
//找小
while (left < right && arr[right] >= kay)
{
--right;
}
//找大
while (left < right && arr[left] <= kay)
{
++left;
}
Swap(&arr[right], &arr[left]);
}
Swap(&arr[left], &kay);
return left;
}
int PartSort1(int* arr, int left, int right)
{
int kayi = left;
while (left < right)
{
//找小
while (left < right && arr[right] >= arr[kayi])
{
--right;
}
//找大
while (left < right && arr[left] <= arr[kayi])
{
++left;
}
Swap(&arr[right], &arr[left]);
}
Swap(&arr[left], &arr[kayi]);
return left;
}
void QuickSort(int* arr, int regin, int end)
{
if (regin >= end)
{
return;
}
int kayi = PartSort1(arr, regin, end);
//左区间 分割下标 右区间
//[regin,kayi-1] kayi [kayi+1,end]
QuickSort(arr, regin, kayi - 1);//递归kayi的左区间
QuickSort(arr, kayi + 1, end);//递归kayi的右区间
}
int GetMidi(int* arr, int left, int right)
{
int mid = (left + right) / 2;
if (arr[left] < arr[mid])
{
if (arr[mid] < arr[right])
{
return mid;
}
else if (arr[right] > arr[left])//mid是最大值的下标
{
return right;
}
else
{
return left;
}
}
else //arr[left] > arr[mid]
{
if (arr[mid] > arr[right])
{
return mid;
}
else if(arr[right] < arr[left]) //mid是最小值的下标
{
return right;
}
else
{
return left;
}
}
}
想来看看挖坑法的动图
- 挖坑法单趟思想:
int PartSort2(int* arr, int left, int right)
{
int midi = GetMidi(arr, left, right);
Swap(&arr[midi], &arr[left]);
int kay = arr[left];
int hole = left;
while (left < right)
{
//找小,找到后与坑位交换,右边形成新的坑
while (left < right && arr[right] >= kay)
{
--right;
}
Swap(&arr[right], &arr[hole]);
hole = right;
//找大,找到后与坑位交换,左边形成新的坑
while (left < right && arr[left] <= kay)
{
++left;
}
Swap(&arr[left], &arr[hole]);
hole = left;
}
arr[hole] = kay;
return hole;
}
- 挖坑法整体思想:
整体思想还是跟hoare版本的整体思想一样,用递归实现
void QuickSort(int* arr, int regin, int end)
{
if (regin >= end)
{
return;
}
int kayi = PartSort2(arr, regin, end);
//左区间 分割下标 右区间
//[regin,kayi-1] kayi [kayi+1,end]
QuickSort(arr, regin, kayi - 1);//递归kayi的左区间
QuickSort(arr, kayi + 1, end);//递归kayi的右区间
}
先看前后指针版本的动图:
- 前后指针版本的单趟思想:
int PartSort3(int* arr, int left, int right)
{
int midi = GetMidi(arr, left, right);
Swap(&arr[midi], &arr[left]);
int prev = left;
int cur = prev + 1;
int kayi = left;
while (cur <= right)
{
if (arr[cur] <= arr[kayi] && ++prev != cur)
{
Swap(&arr[cur], &arr[prev]);
}
++cur;
}
Swap(&arr[prev], &arr[kayi]);
return prev;
}
- 前后指针版本的整体思想:
整体思想也是跟前面两个版本一样的,用递归实现
void QuickSort(int* arr, int regin, int end)
{
if (regin >= end)
{
return;
}
int kayi = PartSort2(arr, regin, end);
//左区间 分割下标 右区间
//[regin,kayi-1] kayi [kayi+1,end]
QuickSort(arr, regin, kayi - 1);//递归kayi的左区间
QuickSort(arr, kayi + 1, end);//递归kayi的右区间
}
- 整体思想的一个优化
- 大家都知道递归深度太深会导致栈溢出,所以当递归到10个数以内的区间后,可以用插入排序进行排序,这样就减少了递归的次数
void QuickSort(int* arr, int begin, int end)
{
if (begin >= end)
{
return;
}
//小区间优化,不在递归分割排序,降低了递归次数
if (begin + end + 1 > 10)
{
int kayi = PartSort3(arr, begin, end);
//[begin, kayi-1] kayi [kayi+1, end]
QuickSort(arr, begin, kayi - 1);
QuickSort(arr, kayi + 1, end);
}
else
{
InsertSort(arr, begin + end + 1);
}
}
用递归排序正常的数据是可以排好的,但在一些特定的情况下,递归深度太深导致栈溢出,那么就需要改成非递归了
- 非递归的快排思想:
void QuickSortNonR(int* arr, int begin, int end)
{
ST st;
STInit(&st);
STPush(&st, end);
STPush(&st, begin);
while (!STEmpty(&st))
{
int left = STTop(&st);
STPop(&st);
int right = STTop(&st);
STPop(&st);
int kayi = PartSort3(arr, left, right);
if (kayi + 1 <= right)
{
STPush(&st, right);
STPush(&st, kayi + 1);
}
if (left <= kayi - 1)
{
STPush(&st, kayi - 1);
STPush(&st, left);
}
}
STDestroy(&st);
- 总结:
- 快速排序的时间复杂度:O(N*logN)
- 快速排序的空间复杂度:O(logN)
- 思路为:
- 开辟一个和原数组一样大的临时空间
- 因为要用递归实现,所以要写一个子函数进行递归式归并
void MergeSort(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
_MergeSort(arr, tmp, 0, n-1);
free(tmp);
tmp = NULL;
}
void _MergeSort(int* arr, int* tmp, int begin, int end)
{
if (begin >= end)
{
return;
}
int mid = (begin + end) / 2;
//[begin, mid] [mid+1, end] 区间的分割
_MergeSort(arr, tmp, begin, mid);//递归左区间
_MergeSort(arr, tmp, mid + 1, end);//递归右区间
//[begin1, end1] [begin2, end2]
int begin1 = begin;
int end1 = mid;
int begin2 = mid + 1;
int end2 = end;
int index = begin;
//左右区间取小的尾插到tmp数组里
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] <= arr[begin2])
{
tmp[index++] = arr[begin1++];
}
else
{
tmp[index++] = arr[begin2++];
}
}
//如果begin1没有小于end1,那么就循环尾插到tmp数组里
while (begin1 <= end1)
{
tmp[index++] = arr[begin1++];
}
//如果begin2没有小于end2,那么就循环尾插到tmp数组里
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
//拷贝到原数组里
memcpy(arr+begin,tmp+begin,sizeof(int)*(end-begin+1));
}
- 用递归写的代码,要有非递归版本,不然在一些特定的情况下递归深度太深会导致栈溢出
- 归并排序的非递归思路:
- 开辟一个和原数组一样大小的空间
- gap从去开始(11归并、22归并、44归并等)
- 单趟思路:
void MergeSortNonR(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
int gap = 1;
int i = 0;
for (i = 0; i < n; i += 2 * gap)
{
//[begin1, end1] [begin2, end2]
int begin1 = i;
int end1 = i + gap - 1;
int begin2 = i + gap;
int end2 = i + 2 * gap - 1;
int index = begin;
//左右区间取小的尾插到tmp数组里
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] <= arr[begin2])
{
tmp[index++] = arr[begin1++];
}
else
{
tmp[index++] = arr[begin2++];
}
}
//如果begin1没有小于end1,那么就循环尾插到tmp数组里
while (begin1 <= end1)
{
tmp[index++] = arr[begin1++];
}
//如果begin2没有小于end2,那么就循环尾插到tmp数组里
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
//拷贝回原数组里
memcpy(arr+i,tmp+i,sizeof(int) * (end2-i+1));
}
free(tmp);
tmp = NULL;
}
- 整体思路:
在加一层循环,条件为gap
void MergeSortNonR(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
int gap = 1;
while (gap < n)
{
int i = 0;
for (i = 0; i < n; i += 2 * gap)
{
//[begin1, end1] [begin2, end2]
int begin1 = i;
int end1 = i + gap - 1;
int begin2 = i + gap;
int end2 = i + 2 * gap - 1;
int index = i;
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++];
}
memcpy(arr+i,tmp+i,sizeof(int)*(end2-i+1));
}
gap *= 2;
}
free(tmp);
tmp = NULL;
}
- 这样归并排序就实现好了,当还没完全实现完
- 8个数据没有出现问题,当9个数据或10个数据时会有越界访问的问题
- 具体是begin1、end1、begin2、end2哪个区间有越界我们不知道,那么就打印一下区间来看看
- 知道了是end1、begin2、end2或begin2、end2或end2在些区间会出现越界后,就加上判断即可
- 判断begin2是否大于等于n,是就break跳出循环,不归并这组数
- 如果只是end2大于等于n,那么修正一下把end2改为n-1即可
void MergeSortNonR(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
int gap = 1;
while (gap < n)
{
int i = 0;
for (i = 0; i < n; i += 2 * gap)
{
//[begin1, end1] [begin2, end2]
int begin1 = i;
int end1 = i + gap - 1;
int begin2 = i + gap;
int end2 = i + 2 * gap - 1;
int index = i;
//如果第二组不存在,那么这一组就不用归并了
if (begin2 >= n)
{
break;
}
//最右边不存在的话,修正一下
if (end2 >= n)
{
end2 = n - 1;
}
//printf("[%d][%d][%d][%d] ", \
begin1, end1, begin2, end2);
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++];
}
memcpy(arr+i,tmp+i,sizeof(int)*(end2-i+1));
}
//printf("\n");
gap *= 2;
}
free(tmp);
tmp = NULL;
}
- 总结:
- 归并排序的时间复杂度:O(N*logN)
- 归并排序的空间复杂度:O(N)
思路:
- 遍历找出最大和最小值
- 计算出开辟空间的大小,然后开辟空间命名为count
- 用原数组的数据减去最小值,然后在count数组里统计出现的次数
- 把count数组统计的次数依次覆盖到原数组里
void CountSort(int* arr, int n)
{
int max = arr[0];
int min = arr[0];
//找最大和最小值
int i = 0;
for (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);//初始化为0
//统计数据出现的次数
for (i = 0; i < n; i++)
{
count[arr[i] - min]++;
}
//排序,依次取出覆盖到原数组里
int j = 0;
for (i = 0; i < range; i++)
{
while (count[i]--)
{
arr[j++] = i + min;
}
}
}
- 总结:
- 计数排序的时间复杂度:O(N + range)
- 计数排序的空间复杂度:O(range)
时间复杂度 | 空间复杂度 | 稳定性 | 不稳定的原因 | |
---|---|---|---|---|
插入排序 | O(N^2) | O(1) | 稳定 | ------------ |
希尔排序 | O(N^1.3) | O(1) | 不稳定 | 预排序时,相同的数据可能分在不同的组里 |
选择排序 | O(N^2) | O(1) | 不稳定 | !不稳定的情况 |
堆排序 | O(N*logN) | O(1) | 不稳定 | !不稳定的情况 |
冒泡排序 | O(N^2) | O(1) | 稳定 | ------------ |
快速排序 | O(N*logN) | O(logN) | 不稳定 | !不稳定的情况 |
归并排序 | O(N*logN) | O()N | 稳定 | ------------ |
#pragma once
#include
#include
//打印函数
void PrintArray(int* arr, int n);
//直接插入排序
void InsertSort(int* arr, int n);
//希尔排序
void ShellSort(int* arr, int n);
//堆排序
void HeapSort(int* arr, int n);
//冒泡排序
void BubbleSort(int* arr, int n);
//直接选择排序
void SelectSort(int* arr, int n);
//快速排序
void QuickSort(int* arr, int begin, int end);
//快速排序 非递归
void QuickSortNonR(int* arr, int begin, int end);
//归并排序
void MergeSort(int* arr, int n);
//归并排序 非递归
void MergeSortNonR(int* arr, int n);
//计数排序
void CountSort(int* arr, int n);
#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"
#include"Stack.h"
void PrintArray(int* arr, int n)
{
int i = 0;
for (i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void InsertSort(int* arr, int n)
{
int i = 0;
for (i = 0; i < n - 1; i++)
{
int end = i;
int tmp = arr[end + 1];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + 1] = arr[end];
}
else
{
break;
}
--end;
}
arr[end + 1] = tmp;
}
}
void ShellSort(int* arr, int n)
{
int gap = n;
while (gap > 1)
{
//gap = gap / 2;
gap = gap / 3 + 1;
int i = 0;
for (i = 0; i < n - gap; i++)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}
}
void AdjustDown(int* arr, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
//找小的孩子
if (child+1 < n && arr[child + 1] > arr[child])
{
++child;
}
if (arr[child] > arr[parent])
{
Swap(&arr[child], &arr[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void HeapSort(int* arr, int n)
{
//向下调整,建堆
int i = 0;
for (i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(arr, n, i);
}
//排序
int end = n - 1;
while (end > 0)
{
Swap(&arr[0], &arr[end]);
AdjustDown(arr, end, 0);
--end;
}
}
void BubbleSort(int* arr, int n)
{
int j = 0;
for (j = 0; j < n; j++)
{
int flag = 0;
int i = 0;
for (i = 1; i < n - j; i++)
{
if (arr[i - 1] > arr[i])
{
Swap(&arr[i - 1], &arr[i]);
flag = 1;
}
}
if (flag == 0)
{
break;
}
}
}
void SelectSort(int* arr, int n)
{
int begin = 0;
int end = n - 1;
while (begin <= end)
{
//[begin, end] 左闭右闭区间
int mini = begin;
int maxi = begin;
int i = 0;
for (i = begin; i < end; i++)
{
if (arr[i] > arr[maxi])
{
maxi = i;
}
if (arr[i] < arr[mini])
{
mini = i;
}
}
Swap(&arr[begin], &arr[mini]);
//max如果被替换了,修正一下
if (maxi == begin)
{
maxi = mini;
}
Swap(&arr[end], &arr[maxi]);
++begin;
--end;
}
}
//三数取中
int GetMidi(int* arr, int left, int right)
{
int mid = (left + right) / 2;
if (arr[mid] > arr[left])
{
if (arr[mid] < arr[right])
{
return mid;
}
else if (arr[left] > arr[right]) //mid是最大值的下标
{
return left;
}
else
{
return right;
}
}
else //arr[mid] < arr[left]
{
if (arr[mid] > arr[right])
{
return mid;
}
else if (arr[left] < arr[right]) //mid是最小值的下标
{
return left;
}
else
{
return right;
}
}
}
//hoare版本
int PartSort1(int* arr, int left, int right)
{
int midi = GetMidi(arr, left, right);
Swap(&arr[midi], &arr[left]);
int kayi = left;
while (left < right)
{
//找小
while (left < right && arr[right] >= arr[kayi])
{
--right;
}
//找大
while (left < right && arr[left] <= arr[kayi])
{
++left;
}
Swap(&arr[left], &arr[right]);
}
Swap(&arr[left], &arr[kayi]);
return left;
}
//挖坑法
int PartSort2(int* arr, int left, int right)
{
int midi = GetMidi(arr, left, right);
Swap(&arr[midi], &arr[left]);
int kay = arr[left];
int hole = left;
while (left < right)
{
//找小,找到后与坑位交换,右边形成新的坑
while (left < right && arr[right] >= kay)
{
--right;
}
Swap(&arr[right], &arr[hole]);
hole = right;
//找大,找到后与坑位交换,左边形成新的坑
while (left < right && arr[left] <= kay)
{
++left;
}
Swap(&arr[left], &arr[hole]);
hole = left;
}
arr[hole] = kay;
return hole;
}
//前后指针版本
int PartSort3(int* arr, int left, int right)
{
int midi = GetMidi(arr, left, right);
Swap(&arr[midi], &arr[left]);
int prev = left;
int cur = prev + 1;
int kayi = left;
while (cur <= right)
{
if (arr[cur] <= arr[kayi] && ++prev != cur)
{
Swap(&arr[cur], &arr[prev]);
}
++cur;
}
Swap(&arr[prev], &arr[kayi]);
return prev;
}
void QuickSort(int* arr, int begin, int end)
{
if (begin >= end)
{
return;
}
//小区间优化,不在递归分割排序,降低了递归次数
if (begin + end + 1 > 10)
{
int kayi = PartSort3(arr, begin, end);
//[begin, kayi-1] kayi [kayi+1, end]
QuickSort(arr, begin, kayi - 1);
QuickSort(arr, kayi + 1, end);
}
else
{
InsertSort(arr, begin + end + 1);
}
}
void QuickSortNonR(int* arr, int begin, int end)
{
ST st;
STInit(&st);
STPush(&st, end);
STPush(&st, begin);
while (!STEmpty(&st))
{
int left = STTop(&st);
STPop(&st);
int right = STTop(&st);
STPop(&st);
int kayi = PartSort3(arr, left, right);
if (kayi + 1 <= right)
{
STPush(&st, right);
STPush(&st, kayi + 1);
}
if (left <= kayi - 1)
{
STPush(&st, kayi - 1);
STPush(&st, left);
}
}
STDestroy(&st);
}
void _MergeSort(int* arr, int* tmp, int begin, int end)
{
if (begin >= end)
{
return;
}
int mid = (begin + end) / 2;
//[begin, mid] [mid+1, end]
_MergeSort(arr, tmp, begin, mid);
_MergeSort(arr, tmp, mid + 1, end);
//[begin1, end1] [begin2, end2]
int begin1 = begin;
int end1 = mid;
int begin2 = mid + 1;
int end2 = end;
int index = begin;
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++];
}
//拷贝到原数组里
memcpy(arr+begin,tmp+begin,sizeof(int)*(end-begin+1));
}
void MergeSort(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
_MergeSort(arr, tmp, 0, n-1);
free(tmp);
tmp = NULL;
}
void MergeSortNonR(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
int gap = 1;
while (gap < n)
{
int i = 0;
for (i = 0; i < n; i += 2 * gap)
{
//[begin1, end1] [begin2, end2]
int begin1 = i;
int end1 = i + gap - 1;
int begin2 = i + gap;
int end2 = i + 2 * gap - 1;
int index = i;
//如果第二组不存在,那么这一组就不用归并了
if (begin2 >= n)
{
break;
}
//最右边不存在的话,修正一下
if (end2 >= n)
{
end2 = n - 1;
}
//printf("[%d][%d][%d][%d] ", \
begin1, end1, begin2, end2);
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++];
}
memcpy(arr + i, tmp + i, sizeof(int) * (end2 - i + 1));
}
//printf("\n");
gap *= 2;
}
free(tmp);
tmp = NULL;
}
//时间复杂度:O(N + range)
//空间复杂度:O(range)
void CountSort(int* arr, int n)
{
int max = arr[0];
int min = arr[0];
//找最大和最小值
int i = 0;
for (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);//初始化为0
//统计数据出现的次数
for (i = 0; i < n; i++)
{
count[arr[i] - min]++;
}
//排序
int j = 0;
for (i = 0; i < range; i++)
{
while (count[i]--)
{
arr[j++] = i + min;
}
}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"
void TestInsertSort()
{
int arr[] = { 9,5,3,7,2,8,1,4,5,6,10 };
int size = sizeof(arr) / sizeof(arr[0]);
InsertSort(arr, size);
PrintArray(arr, size);
}
void TestShellSort()
{
int arr[] = { 9,1,2,5,7,4,8,6,3,5 };
int size = sizeof(arr) / sizeof(arr[0]);
ShellSort(arr, size);
PrintArray(arr, size);
}
void TestHeapSort()
{
int arr[] = { 9,1,2,5,7,4,8,6,3,5 };
int size = sizeof(arr) / sizeof(arr[0]);
HeapSort(arr, size);
PrintArray(arr, size);
}
void TestBubbleSort()
{
int arr[] = { 9,1,2,5,7,4,8,6,3,5 };
int size = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, size);
PrintArray(arr, size);
}
void TestSelectSort()
{
int arr[] = { 9,1,2,5,7,4,8,6,3,5 };
int size = sizeof(arr) / sizeof(arr[0]);
SelectSort(arr, size);
PrintArray(arr, size);
}
void TestQuickSort()
{
int arr[] = {6,1,2,7,9,3,4,5,10,8 };
int size = sizeof(arr) / sizeof(arr[0]);
QuickSort(arr, 0, size - 1);
PrintArray(arr, size);
}
void TestQuickSortNonRSort()
{
int arr[] = { 6,1,2,7,9,3,4,5,10,8 };
int size = sizeof(arr) / sizeof(arr[0]);
QuickSortNonR(arr, 0, size-1);
PrintArray(arr, size);
}
void TestMergeSort()
{
int arr[] = { 10,6,7,1,3,9,4,2 };
int size = sizeof(arr) / sizeof(arr[0]);
MergeSort(arr, size);
PrintArray(arr, size);
}
void TestMergeSortNonR()
{
int arr[] = { 6,7,1,3,9,4,2,5,9,1,1 };
int size = sizeof(arr) / sizeof(arr[0]);
MergeSortNonR(arr, size);
PrintArray(arr, size);
}
void TestCountSort()
{
int arr[] = { 6,7,1,3,9,4,2,5,9,1,1 };
int size = sizeof(arr) / sizeof(arr[0]);
CountSort(arr, size);
PrintArray(arr, size);
}
int main()
{
//TestInsertSort();
//TestShellSort();
//TestHeapSort();
//TestBubbleSort();
//TestSelectSort();
//TestQuickSort();
//TestQuickSortNonRSort();
//TestMergeSort();
//TestMergeSortNonR();
TestCountSort();
return 0;
}