版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LunaW/article/details/83383505
前言:排序的定义
排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。分内部排序和外部排序,若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。内部排序的过程是一个逐步扩大记录的有序序列长度的过程
各种排序的时间复杂度
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
简单选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 稳定 |
直接插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
希尔排序 | O(nlogn)~O(n^2) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O(n^2) | O(logn)~O(n) | 不稳定 |
各种排序的时间测试
排序方法 | 5万数据 | 50万数据 | 100 万数据 |
---|---|---|---|
优化冒泡排序 | 8.183984s | 太慢 | 太慢 |
简单选择排序 | 2.682234s | 太慢 | 太慢 |
直接插入排序 | 1.496184s | 太慢 | 太慢 |
希尔插入排序 | 0.008725s | 0.120478s | 0.256040s |
顶堆选择排序 | 0.007705s | 0.097579s | 0.204354s |
递归归并排序 | 0.006827s | 栈溢出 | 栈溢出 |
非递归并排序 | 0.006666s | 0.080683s | 0.148987s |
普通快速排序 | 0.007767s | 0.086778s | 0.188957s |
优化快速排序 | 0.007215s | 0.087385s | 0.182627s |
冒泡排序
- 冒泡排序,通过两两比较相邻记录,依次浮出最小或者最大值
- 优化方案:增加标志位判断是否已经有序,而不继续冒泡
实现代码
int BubbleSort1(SqList * L)
{
int i, j;
int flag = 1;
for (i = 0; i < MAXSIZE - 1 && flag; i++) {
flag = 0; // 若标志位flag不为1,说明已经有序,这个时候就可以退出循环了
for (j = MAXSIZE - 1; j > i; j--) {
if (L->r[j] < L->r[j - 1]) {
swap(L, j, j - 1);
flag = 1;
}
}
}
return 0;
}
简单选择排序
- 简单选择排序,通过n-i次关键字的比较,从n-i+1个记录中选出关键字最小的记录,然后与第i的位置替换
- 通俗的讲就是,循环比较出后面最小的值依次放在前面去,就是从小到大了
实现代码
int SelectSort(SqList * L)
{
int i, j, min;
for (i = 0; i < MAXSIZE - 1; i++) {
min = i; // 将最小值的下标min指向当前下标i,起始位置
for (j = i + 1; j < MAXSIZE; j++) {
if (L->r[min] > L->r[j]) {
min = j; // 将最小值的下标min指向下标j,不停的比较,min指向当前循环最小值的下标
}
}
if (i != min) {
swap(L, i, min); // 将最小值min交换到有序的i的位置,则每次循环以后,最小的值都依次被排序
}
}
return 0;
}
直接插入排序
- 直接插入排序,从第二个数开始循环,将当前数拿出来,依次将左边大于当前值的数向后移动一位,直到不大于当前值停止,然后将当前值插入空档
实现代码
int InsertSort(SqList * L)
{
int i, j, flag;
for (i = 1; i < MAXSIZE; i++) { // 从下标1开始,假设下标0已经有序
if (L->r[i] < L->r[i - 1]) { // 若当前的值小于前一位,则执行下面的操作
flag = L->r[i]; // 将当前值记录到储存位
for (j = i - 1; j >= 0 && L->r[j] > flag; j--) { // 将左边大于当前值的依次向后移动一位,直到不大于为止
L->r[j + 1] = L->r[j];
}
L->r[j + 1] = flag; // 插入储存位到空档
}
}
return 0;
}
希尔排序
- 希尔排序,直接插入排序的高效版,先使整个序列基本有序,然后再进行插入排序,大突破,打破O[n^2]
- 使基本有序的方法就是把顺序循环变成跳跃方式的间隔循环,例如a[i]和a[i+1]比较变成a[i]和a[i+4]比较,然后慢慢缩小a[i]和a[i+2]比较,最后为1的时候就是插入排序了,这个过程在逐渐把排序变成基本有序
实现代码
int ShellSort(SqList * L)
{
int i, j, flag;
int increment = MAXSIZE;
while (increment > 1) {
increment = increment / 3 + 1; // 增量,目前没有最好的增量,可以调整
for (i = increment; i < MAXSIZE; i++) { // 跳跃调整序列为基本有序,increment等于1时为普通插入排序
if (L->r[i] < L->r[i - increment]) {
flag = L->r[i]; // 将当前值记录到储存位
for (j = i - increment; j >= 0 && L->r[j] > flag; j -= increment) {
L->r[j + increment] = L->r[j]; // 记录后移,查找插入位置
}
L->r[j + increment] = flag; //插入储存位到空档
}
}
}
return 0;
}
堆排序,归并排序
- 不做仔细讲解,这两个略麻烦
- 堆排序利用二叉树构建大顶堆取根节点
- 归并排序采用拆分比较,再归并的方式实现排序
实现代码
// 见完整代码
快速排序
- 王者段位的排序,冒泡排序的升级版
- 中心思想就是递归不断的把小的放左边,大的放右边,最后实现排序
实现代码
int QuickSort(SqList * L)
{
QSort(L, 0, MAXSIZE - 1);
return 0;
}
int QSort(SqList * L, int low, int high)
{
int pivot;
if (low < high) {
pivot = Partition(L, low, high); // 将low..high一分为二
QSort(L, low, pivot - 1); // 向低子表递归排序
QSort(L, pivot + 1, high); // 向高子表递归排序
}
return 0;
}
int Partition(SqList * L, int low, int high)
{
int pivotkey;
pivotkey = L->r[low]; //先暂时定第一个位置为中枢。此时选取可优化,因为可能不是中间值,可能偏大偏小
while (low < high) { // 不停的移动下标,当上下位置移动相交以后退出循环,此时中枢前后分别小于和大于中枢值
while (low < high && L->r[high] >= pivotkey) { // 从上向下直到当前值小于中枢值
high--;
}
swap(L, low, high); // 把上半区中比中枢记录小的交换到低端
while (low < high && L->r[low] <= pivotkey) { // 从下向上直到当前值大于中枢值
low++;
}
swap(L, low, high); // 把下半区中比中枢记录大的交换到顶端
}
return low; // 返回中枢下标位置
}
完整代码
/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
> Author: xiaojunyu/LunaW
> Mail : [email protected]
> Gmail : [email protected]
> Blog : http://blog.csdn.net/lunaw
> Web : http://lunaw.cn
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#define MAXSIZE 50000 // 设置数据量
#define IsPrintf 0 // 设置为1可以查看输出
typedef struct {
int r[MAXSIZE + 1];
} SqList;
void Init(SqList * L); // 初始化随机数
void Print(SqList * L); // 输出数据
void swap(SqList * L, int i, int j); // 交换值
double GetTime(void); // 获取当前时间
int BubbleSort0(SqList * L);
/* 最简单的交换排序,不是真正意义上的冒泡排序 */
int BubbleSort(SqList * L);
/* 真正意义上的冒泡排序,两两交换,浮出最小或者最大值 */
int BubbleSort1(SqList * L);
/* 优化的冒泡排序,浮出最小或者最大值,增加标志位判断是否已经有序 */
int SelectSort(SqList * L);
/* 简单选择排序,通过n-i次关键字的比较,从n-i+1个记录中选出关键字最小的记录,然后与第i的位置替换 */
int InsertSort(SqList * L);
/* 直接插入排序,从第二个数开始循环,将当前数拿出来,依次将左边大于当前值的数向后移动一位,直到不大于当前值停止,然后将当前值插入空档 */
int ShellSort(SqList * L);
/* 希尔排序,直接插入排序的高效版,先使整个序列基本有序,然后再进行插入排序,大突破,打破O[n的平方] */
int HeapAdjust(SqList * L, int s, int m); // 构建大顶堆
int HeapSort(SqList * L);
/* 堆排序,简单选择排序的升级*/
int MSort(int SR[], int TR1[], int s, int t);
int Merge(int SR[], int TR[], int i, int m, int n);
int MergeSort(SqList * L);
/* 递归归并排序 */
int MergePass(int SR[], int TR[], int s, int n);
int MergeSort1(SqList * L);
/* 非递归并排序 */
int Partition(SqList * L, int low, int high); // 选取中枢,然后使左边都比它小,右边都比它大
int QSort(SqList * L, int low, int high); // 递归排序
int QuickSort(SqList * L);
/* 快速排序,冒泡排序的升级,王者段位的排序,中心思想就是递归不断的把小的放左边,大的放右边*/
int Partition1(SqList * L, int low, int high); // 三数取中法选取中枢,使左边都比它小,右边都比它大
int QSort1(SqList * L, int low, int high); // 递归排序
int QuickSort1(SqList * L);
/* 优化快速排序,采用三数取中法选取中枢,同时减少中枢的交换次数 */
int main(void)
{
double start, end;
SqList H;
SqList L;
printf("数据量: %d\n\n", MAXSIZE);
Init(&H);
// printf("原始数据: \n");
// Print(&H);
// L = H;
// start = GetTime();
// BubbleSort0(&L);
// end = GetTime();
// printf("初级冒泡排序: \n");
// Print(&L);
// printf("运行时间: %lfs\n\n", end - start);
// L = H;
// start = GetTime();
// BubbleSort(&L);
// end = GetTime();
// printf("标准冒泡排序: \n");
// Print(&L);
// printf("运行时间: %lfs\n\n", end - start);
if (MAXSIZE <= 50000) { // 数据大了时间太久
L = H;
start = GetTime();
BubbleSort1(&L);
end = GetTime();
printf("优化冒泡排序: \n");
Print(&L);
printf("运行时间: %lfs\n\n", end - start);
L = H;
start = GetTime();
SelectSort(&L);
end = GetTime();
printf("简单选择排序: \n");
Print(&L);
printf("运行时间: %lfs\n\n", end - start);
L = H;
start = GetTime();
InsertSort(&L);
end = GetTime();
printf("直接插入排序: \n");
Print(&L);
printf("运行时间: %lfs\n\n", end - start);
}
L = H;
start = GetTime();
ShellSort(&L);
end = GetTime();
printf("希尔插入排序: \n");
Print(&L);
printf("运行时间: %lfs\n\n", end - start);
L = H;
start = GetTime();
HeapSort(&L);
end = GetTime();
printf("顶堆选择排序: \n");
Print(&L);
printf("运行时间: %lfs\n\n", end - start);
if (MAXSIZE <= 100000) { // 数组递归把栈内存占满了会报错,分配内存又很慢
L = H;
start = GetTime();
MergeSort(&L);
end = GetTime();
printf("递归归并排序: \n");
Print(&L);
printf("运行时间: %lfs\n\n", end - start);
}
L = H;
start = GetTime();
MergeSort1(&L);
end = GetTime();
printf("非递归并排序: \n");
Print(&L);
printf("运行时间: %lfs\n\n", end - start);
L = H;
start = GetTime();
QuickSort(&L);
end = GetTime();
printf("普通快速排序: \n");
Print(&L);
printf("运行时间: %lfs\n\n", end - start);
L = H;
start = GetTime();
QuickSort1(&L);
end = GetTime();
printf("优化快速排序: \n");
Print(&L);
printf("运行时间: %lfs\n\n", end - start);
return 0;
}
void Print(SqList * L)
{
if (IsPrintf) {
int i;
for (i = 0; i < MAXSIZE; i++) {
printf("%d ", L->r[i]);
}
printf("\n");
}
}
void Init(SqList * L)
{
int i;
srand((unsigned)time(NULL));
for (i = 0; i < MAXSIZE; i++) {
L->r[i] = rand() % MAXSIZE;
}
}
void swap(SqList * L, int i, int j)
{
int temp = L->r[i];
L->r[i] = L->r[j];
L->r[j] = temp;
}
int BubbleSort1(SqList * L)
{
int i, j;
int flag = 1;
for (i = 0; i < MAXSIZE - 1 && flag; i++) {
flag = 0; // 若标志位flag不为1,说明已经有序,这个时候就可以退出循环了
for (j = MAXSIZE - 1; j > i; j--) {
if (L->r[j] < L->r[j - 1]) {
swap(L, j, j - 1);
flag = 1;
}
}
}
return 0;
}
int BubbleSort(SqList * L)
{
int i, j;
for (i = 0; i < MAXSIZE - 1; i++) {
for (j = MAXSIZE - 1; j > i; j--) {
if (L->r[j] < L->r[j - 1]) {
swap(L, j, j - 1);
}
}
}
return 0;
}
int BubbleSort0(SqList * L)
{
int i, j;
for (i = 0; i < MAXSIZE - 1; i++) {
for (j = i + 1; j < MAXSIZE; j++) {
if (L->r[i] > L->r[j]) {
swap(L, i, j);
}
}
}
return 0;
}
int InsertSort(SqList * L)
{
int i, j, flag;
for (i = 1; i < MAXSIZE; i++) { // 从下标1开始,假设下标0已经有序
if (L->r[i] < L->r[i - 1]) { // 若当前的值小于前一位,则执行下面的操作
flag = L->r[i]; // 将当前值记录到储存位
for (j = i - 1; j >= 0 && L->r[j] > flag; j--) { // 将左边大于当前值的依次向后移动一位,直到不大于为止
L->r[j + 1] = L->r[j];
}
L->r[j + 1] = flag; // 插入储存位到空档
}
}
return 0;
}
int ShellSort(SqList * L)
{
int i, j, flag;
int increment = MAXSIZE;
while (increment > 1) {
increment = increment / 3 + 1; // 增量,目前没有最好的增量,可以调整
for (i = increment; i < MAXSIZE; i++) { // 跳跃调整序列为基本有序,increment等于1时为普通插入排序
if (L->r[i] < L->r[i - increment]) {
flag = L->r[i]; // 将当前值记录到储存位
for (j = i - increment; j >= 0 && L->r[j] > flag; j -= increment) {
L->r[j + increment] = L->r[j]; // 记录后移,查找插入位置
}
L->r[j + increment] = flag; //插入储存位到空档
}
}
}
return 0;
}
int SelectSort(SqList * L)
{
int i, j, min;
for (i = 0; i < MAXSIZE - 1; i++) {
min = i; // 将最小值的下标min指向当前下标i,起始位置
for (j = i + 1; j < MAXSIZE; j++) {
if (L->r[min] > L->r[j]) {
min = j; // 将最小值的下标min指向下标j,不停的比较,min指向当前循环最小值的下标
}
}
if (i != min) {
swap(L, i, min); // 将最小值min交换到有序的i的位置,则每次循环以后,最小的值都依次被排序
}
}
return 0;
}
double GetTime(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + (double)tv.tv_usec / 1000000;
}
// 在L->r[s..m]中除l->r[s]外都满足堆的定义的情况下,调整L->r[s]的值,使其成为大顶堆
int HeapAdjust(SqList * L, int s, int m)
{
int temp, j;
temp = L->r[s];
for (j = 2 * s; j <= m; j *= 2) { // 沿关键字较大的孩子节点向下筛选
if (j < m && L->r[j] < L->r[j + 1]) {
++j; // j为关键字中较大的记录的下标
}
if (temp >= L->r[j]) {
break;
}
L->r[s] = L->r[j];
s = j;
}
L->r[s] = temp; // 插入
return 0;
}
int HeapSort(SqList * L)
{
int i;
for (i = (MAXSIZE - 1) / 2; i >= 0; i--) { // 构建一个大顶堆
HeapAdjust(L, i, MAXSIZE - 1);
}
for (i = MAXSIZE - 1; i >= 1; i--) { // 反复将堆顶移到最后有序的位置,最后排序完成
swap(L, 0, i); // 将堆顶与后面未排序的最后一位交换
HeapAdjust(L, 0, i - 1); // 将交换后的序列重新调整为成大顶堆
}
return 0;
}
int MergeSort1(SqList * L)
{
int *TR = (int *)malloc(MAXSIZE * sizeof(int));
int k = 1;
while (k < MAXSIZE) {
MergePass(L->r, TR, k, MAXSIZE);
k = 2 * k; // 子序列长度加倍
MergePass(TR, L->r, k, MAXSIZE);
k = 2 * k; // 子序列长度加倍
}
return 0;
}
int MergePass(int SR[], int TR[], int s, int n)
{
int i = 1;
int j;
while (i <= n - 2 * s + 1) {
Merge(SR, TR, i - 1, i + s - 1 - 1, i + 2 * s - 1 - 1);
i = i + 2 * s;
}
if (i < n - s + 1) {
Merge(SR, TR, i - 1, i + s - 1 - 1, n - 1);
} else {
for (j = i; j <= n; j++) {
TR[j - 1] = SR[j - 1];
}
}
return 0;
}
int MergeSort(SqList * L)
{
MSort(L->r, L->r, 0, MAXSIZE - 1);
return 0;
}
int MSort(int SR[], int TR1[], int s, int t)
{
int m;
int TR2[MAXSIZE + 1];
if (s == t) {
TR1[s] = SR[s];
} else {
m = (s + t) / 2;
MSort(SR, TR2, s, m);
MSort(SR, TR2, m + 1, t);
Merge(TR2, TR1, s, m, t);
}
return 0;
}
int Merge(int SR[], int TR[], int i, int m, int n)
{
int j, k, l;
for (j = m + 1, k = i; i <= m && j <= n; k++) {
if (SR[i] < SR[j]) {
TR[k] = SR[i++];
} else {
TR[k] = SR[j++];
}
}
if (i <= m) {
for (l = 0; l <= m - i; l++) {
TR[k + l] = SR[i + l];
}
}
if (i <= n) {
for (l = 0; l <= n - j; l++) {
TR[k + l] = SR[j + l];
}
}
return 0;
}
int Partition(SqList * L, int low, int high)
{
int pivotkey;
pivotkey = L->r[low]; //先暂时定第一个位置为中枢。此时选取可优化,因为可能不是中间值,可能偏大偏小
while (low < high) { // 不停的移动下标,当上下位置移动相交以后退出循环,此时中枢前后分别小于和大于中枢值
while (low < high && L->r[high] >= pivotkey) { // 从上向下直到当前值小于中枢值
high--;
}
swap(L, low, high); // 把上半区中比中枢记录小的交换到低端
while (low < high && L->r[low] <= pivotkey) { // 从下向上直到当前值大于中枢值
low++;
}
swap(L, low, high); // 把下半区中比中枢记录大的交换到顶端
}
return low; // 返回中枢下标位置
}
// 优化函数
int Partition1(SqList * L, int low, int high)
{
int pivotkey;
/* 三数取中法 使中枢更接近中间值 */
int m = low + (high - low) / 2;
if (L->r[low] > L->r[high]) {
swap(L, low, high);
}
if (L->r[m] > L->r[high]) {
swap(L, high, m);
}
if (L->r[m] > L->r[low]) {
swap(L, m, low);
}
pivotkey = L->r[low]; //先暂时定第一个位置为中枢。此时选取可优化,因为可能不是中间值,可能偏大偏小
L->r[MAXSIZE] = pivotkey; // [减少中枢交换优化] 先保存中枢到备份位
while (low < high) { // 不停的移动下标,当上下位置移动相交以后退出循环,此时中枢前后分别小于和大于中枢值
while (low < high && L->r[high] >= pivotkey) { // 从上向下直到当前值小于中枢值
high--;
}
L->r[low] = L->r[high]; // [减少中枢交换优化] 改交换为替换,可以减少交换次数
while (low < high && L->r[low] <= pivotkey) { // 从下向上直到当前值大于中枢值
low++;
}
L->r[high] = L->r[low]; // [减少中枢交换优化] 改交换为替换,可以减少交换次数
}
L->r[low] = L->r[MAXSIZE]; // [减少中枢交换优化] 将中枢数据替换回来
return low; // 返回中枢下标位置
}
int QSort(SqList * L, int low, int high)
{
int pivot;
if (low < high) {
pivot = Partition(L, low, high); // 将low..high一分为二
QSort(L, low, pivot - 1); // 向低子表递归排序
QSort(L, pivot + 1, high); // 向高子表递归排序
}
return 0;
}
int QSort1(SqList * L, int low, int high)
{
int pivot;
if (low < high) {
pivot = Partition1(L, low, high); // 将low..high一分为二
QSort(L, low, pivot - 1); // 向低子表递归排序
QSort(L, pivot + 1, high); // 向高子表递归排序
}
return 0;
}
int QuickSort(SqList * L)
{
QSort(L, 0, MAXSIZE - 1);
return 0;
}
int QuickSort1(SqList * L)
{
QSort1(L, 0, MAXSIZE - 1);
return 0;
}