插入排序的实现:
//插入排序:时间复杂度最大为o(N*N);最小为o(N);不稳定;
void insert(int* arr, int n)
{
for (int i = 1; i < n; ++i)
{
int end = i - 1;
//保存要插入的数据
int data = arr[i];
//如果插入的数据小于有序列的最后一个数,则将最后一个数向后移动;并让插入数据继续向前对比;
while (end >= 0 && arr[end] >= data)
{
arr[end + 1] = arr[end];
end--;
}
arr[end + 1] = data;
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
insert(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
希尔排序与插入排序原理相同,步长不同。
//希尔排序:时间复杂度o(N^1.3);不稳定
void shell(int* arr, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3+1;//定义每一次排序的两个数据之间的距离
for (int i = gap; i < n; i++)
{
int end = i - gap;//有序数列中的最后一个数据
int data = arr[i];
while (end >= 0 && arr[end] > data)//若最后一个数据大于同组的另一个数据,则将最后一个数据放在另一个数据之后,并更新end的值
{
arr[end + gap] = arr[end];
end = end - gap;
}
arr[end + gap] = data;//若最后一个数据小于同组数据,则将该数据插入到end+gap的位置
}
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
shell(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
直接排序:
//直接排序,时间复杂度o(N*N),空间复杂度o(1),不稳定。
void swap(int* arr, int pos1, int pos2)
{
int tmp = arr[pos1];
arr[pos1] = arr[pos2];
arr[pos2] = tmp;
}
void select(int* arr, int n)
{
int start = 0;
int end = n - 1;
//每次将最小值放在起始位置,下次比较时不再比较已确定的最小值。
while (start < end)
{
int min = start;
int i;
for (i=start+1; i < n; ++i)
{
if(arr[i] < arr[min])
{
min = i;
}
}
int tmp = arr[start];
arr[start] = arr[min];
arr[min] = tmp;
++start;
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
select(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
优化直接排序:同时得到最大值和最小值。
//优化直接排序
void swap(int* arr, int pos1, int pos2)//交换函数
{
int tmp = arr[pos1];
arr[pos1] = arr[pos2];
arr[pos2] = tmp;
}
void Select(int* arr, int n)//优化后同时将最大值和最小值得到
{
int start = 0;
int end = n - 1;
while (start < end)
{
int min = start;
int max = start;
for (int i = start + 1; i <=end; i++)
{
if (arr[i] > arr[max])
{
max = i;
}
if (arr[i] < arr[min])
{
min = i;
}
}
swap(arr, start, min);//得到最小值后将最小值放在最前面
if (max == start)//若最大值就在第一个位置,则将其与最小值交换,否则会使最大值丢失
max = min;
swap(arr, end, max);//把最大值放在最后面
start++;//排除第一个和最后一个数据继续进行排序
end--;
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
Select(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
堆排序
//堆排序,时间复杂度o(N*logN),空间复杂度o(1),不稳定
void shiftdown(int* arr, int n, int root)//若向下调整为最大堆,进行排序后数组数据排序方式为从小到大,若为最小堆,则排序方式为从大到小。
{
int child = 2 * root + 1;//左孩
while (child<n)
{
if (arr[child + 1] > arr[child] && child + 1 < n)
{
child++;//若右孩大于左孩且右孩没有超出数组范围,则将父节点与右孩比较;
}
if (arr[root] < arr[child])//调整为最大堆
{
int tmp = arr[root];//交换后继续从右孩处进行比较
arr[root] = arr[child];
arr[child] = tmp;
root = child;
child = 2 * root + 1;
}
else//若父节点比右孩大则结束循环
break;
}
}
void heap(int* arr, int n)
{
for (int i = (n - 2) / 2;i >= 0; --i)//从最后一个非叶子结点开始向上调整,调整到根结点;每次调整都将该非叶子结点的结构变为最大堆,结束循环后该数组即为最大堆
{
shiftdown(arr, n, i);
}
int end = n - 1;//进行排序,每次将最大值放到数组最后一个位置,并在下次排序时不将该最大值考虑在内
while (end > 0)
{
int tmp = arr[end];
arr[end] = arr[0];
arr[0] = tmp;
shiftdown(arr, end, 0);
--end;
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
heap(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
冒泡排序
//冒泡排序,时间复杂度o(N^2),空间复杂度o(1),稳定。
void Bubble(int* arr, int n)
{
int end = n;
while (end > 1)//进行一次循环后,需要比较的数据就少一个。
{
int flag = 0;//判断是否进行了数据的交换,若没有则说明排序已完成,结束循环。
for (int i = 0; i < end-1; i++)
{
if (arr[i] > arr[i + 1])
{
int tmp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = tmp;
flag = 1;
}
}
if(!flag)
break;
end--;
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
Bubble(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
快速排序霍尔法:
//快速排序递归实现,hoare法,不稳定
int part1(int* arr, int left, int right)
{
int mid = getMid(arr, left, right);//获取基准值的位置
//把基准值放到起始位置
int tmp = arr[left];
arr[left] = arr[mid];
arr[mid] = tmp;
int idx = arr[left];
int start = left;
while (left < right)
{
while (left < right&&arr[right] >= idx)//从后往前找第一个小于基准值的数据
{
right--;
}
while (left < right&&arr[left] <= idx)//从前往后找第一个大于基准值的数据
{
left++;
}
int tmp = arr[right];//交换两个数据
arr[right] = arr[left];
arr[left] = tmp;
}
int x = arr[start];//结束时将left和right相遇的位置的数据和基准值交换,此时基准值左边的值都小于基准值,右边的值都大于基准值;
arr[start] = arr[left];
arr[left] = x;
return left;//返回此时基准值所在的位置
}
void part(int* arr, int left, int right)
{
if (left >= right)
return;
int div = part1(arr, left, right);
part(arr, left, div - 1);//[left,div-1)
part(arr, div + 1, right);//(div+1,right]
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
part(arr, 0,n-1);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
优化快速排序基准值获取方法
//快速排序优化,三数取中法确定基准值,起始位置,中间位置,结束位置;数据有序时,如果没有优化,可能会导致栈溢出,因为栈调用的层数约等于数据的个数,优化之后,划分比较均衡,时间复杂度为o(n*logn),栈的调用层数约等于logn;
//稳定性:不稳定
int getMid(int* arr, int left, int right)
{
int mid = left + (right - left) / 2;
if (arr[left] > arr[mid])
{
if (arr[mid] > arr[right])
return mid;
else if (arr[left] > arr[right])
{
return right;
}
else
return left;
}
else
{
if (arr[mid] < arr[right])
{
return mid;
}
else if (arr[left] < arr[right])
{
return right;
}
else
return left;
}
}
快速排序挖坑法
//快速排序挖坑法
int getmid(int* arr, int left, int right)
{
int mid = left + (right + left) / 2;
if (arr[mid] > arr[left])
{
if (arr[mid] < arr[right])
{
return mid;
}
else if (arr[left] > arr[right])
{
return left;
}
else
{
return right;
}
}
else
{
if (arr[mid] > arr[right])
{
return mid;
}
else if (arr[left] < arr[right])
{
return left;
}
else
return right;
}
}
int part2(int* arr, int left, int right)
{
int mid = getmid(arr, left, right);
int tmp = arr[mid];
arr[mid] = arr[left];
arr[left] = tmp;
int key = arr[left];
//从后往前找到第一个小于基准值的位置,将其值放在基准值的位置,此时该位置空了出来;再从前往后找到第一个大于基准值的位置,将其值放在上一步运行后空出来的位置上;
while (left < right)
{
while (left < right&&arr[right] >= key)
{
right--;
}
arr[left] = arr[right];
while (left < right&&arr[left] <= key)
{
left++;
}
arr[right] = arr[left];
}
arr[left] = key;
return left;//返回基准值所在的位置。
}
void part(int* arr, int left, int right)
{
if (left >= right)
return;
//找到分割小于基准值和大于基准值的位置;并将左边小于基准值的所有值和右边大于基准值的所有值继续进行挖坑排序。
int div = part2(arr, left, right);
part(arr, left, div - 1);
part(arr, div + 1, right);
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
part(arr, 0, n - 1);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
快速排序前后指针法:
//快速排序前后指针法
int part3(int* arr, int left, int right)
{
int prev = left;//上一个小于基准值的位置
int cur = left+1;//下一个小于基准值的位置
int key = arr[left];
while (cur <= right)
//若cur位置的值大于基准值,则cur向后移动;
//否则,若cur和prev连续,则将两者同时向后移动继续进行cur位置与基准值的比较;
//若不连续,则将prev向后移动一位并与cur位置的值进行交换,再将cur向后移动
{
if (arr[cur] > key)
{
cur++;
}
else
{
if (cur == (prev + 1))
{
prev++;
cur++;
}
else
{
prev++;
int tmp = arr[prev];
arr[prev] = arr[cur];
arr[cur] = tmp;
cur++;
}
}
}
//当cur的位置移动到超出数组范围时,将基准值与prev位置的值进行交换;此时左边为小于基准值的值,右边为大于基准值的值。
int tmp = arr[left];
arr[left] = arr[prev];
arr[prev] = tmp;
return prev;
}
void part(int* arr, int left, int right)
{
if (left >= right)
return;
int div = part3(arr, left, right);
part(arr, left, div - 1);
part(arr, div + 1, right);
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
part(arr, 0, n - 1);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
快速排序非递归法:用顺序表实现。顺序表和队列的相关函数声明在下一篇文章中进行介绍。
//快速排序非递归法,用顺序表实现
int getmid(int* arr, int left, int right)
{
int mid = left + (right + left) / 2;
if (arr[mid] > arr[left])
{
if (arr[mid] < arr[right])
{
return mid;
}
else if (arr[left] > arr[right])
{
return left;
}
else
{
return right;
}
}
else
{
if (arr[mid] > arr[right])
{
return mid;
}
else if (arr[left] < arr[right])
{
return left;
}
else
return right;
}
}
int part2(int* arr, int left, int right)
{
int mid = getmid(arr, left, right);
int tmp = arr[mid];
arr[mid] = arr[left];
arr[left] = tmp;
int key = arr[left];
while (left < right)
{
while (left < right&&arr[right] >= key)
{
right--;
}
arr[left] = arr[right];
while (left < right&&arr[left] <= key)
{
left++;
}
arr[right] = arr[left];
}
arr[left] = key;
return left;
}
/*排序的原理一样,但是在处理小于基准值和大于基准值的所有值的步骤中有所不同。
非递归方法利用顺序表,将数组的起始位置和结束位置放入顺序表中(注意先放结束位置再放起始位置),每次循环判断顺序表是否为空,若不为空,则将顺序表中的数据取出,取出的第一个元素为待排序数组的起始位置,第二个元素为结束位置,
取出后即删除顺序表中的这两个元素,并对这两个位置之间的数据进行排序,得到下一次待排序数组的基准值,判断上次取出的起始位置是否小于排序后小于基准值的一侧数据的结束位置,若小于,则将小于基准值一侧数据的起始位置和结束位置尾插进顺序表中,对大于基准值一侧的数据进行同样的操作,直到顺序表为空,即表示排序完成。*/
void QuickSortnonr(int* arr, int n)
{
SeqList sq;
SeqListinit(&sq);
SeqListPushBack(&sq, n - 1);
SeqListPushBack(&sq, 0);
while (!SeqListEmpty(&sq))
{
int left = SeqListBack(&sq);
SeqListPopBack(&sq);
int right = SeqListBack(&sq);
SeqListPopBack(&sq);
int div = part2(arr, left, right);
if (left < div - 1)
{
SeqListPushBack(&sq, div - 1);
SeqListPushBack(&sq, left);
}
if (div + 1 < right)
{
SeqListPushBack(&sq, right);
SeqListPushBack(&sq, div + 1);
}
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
QuickSortnonr(arr,n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
用队列实现:用队列实现和用顺序表实现的区别就在于起始和结束位置存入数据结构的顺序,队列是先存入起始位置,再存入结束位置。因为队列是先入先出。
//快速排序非递归法,用队列实现
int getmid(int* arr, int left, int right)
{
int mid = left + (right + left) / 2;
if (arr[mid] > arr[left])
{
if (arr[mid] < arr[right])
{
return mid;
}
else if (arr[left] > arr[right])
{
return left;
}
else
{
return right;
}
}
else
{
if (arr[mid] > arr[right])
{
return mid;
}
else if (arr[left] < arr[right])
{
return left;
}
else
return right;
}
}
int part2(int* arr, int left, int right)
{
int mid = getmid(arr, left, right);
int tmp = arr[mid];
arr[mid] = arr[left];
arr[left] = tmp;
int key = arr[left];
while (left < right)
{
while (left < right&&arr[right] >= key)
{
right--;
}
arr[left] = arr[right];
while (left < right&&arr[left] <= key)
{
left++;
}
arr[right] = arr[left];
}
arr[left] = key;
return left;
}
void QuickSortnonr(int* arr, int n)
{
Queue q;
queueinit(&q);
queuePush(&q, 0);
queuePush(&q, n-1);
while (!queueempty(&q))
{
int left = queueFront(&q);
queuePop(&q);
int right = queueFront(&q);
queuePop(&q);
int div = part2(arr, left, right);
if (left < div - 1)
{
queuePush(&q, left);
queuePush(&q, div-1);
}
if (div + 1 < right)
{
queuePush(&q, div+1);
queuePush(&q, right);
}
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
QuickSortnonr(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
归并排序递归法:
//归并排序递归法,时间复杂度o(N*logN),稳定。
void Merge(int* arr, int begin,int mid,int end,int* tmp)
{
int begin1 = begin;
int end1 = mid;
int begin2 = end1 + 1;
int end2 = end;
//辅助空间的起始位置
int idx = begin;
//合并有序序列
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] <= arr[begin2])
{
tmp[idx] = arr[begin1];
idx++; begin1++;
}
else
{
tmp[idx++] = arr[begin2++];
}
}
//判断是否有未合并的元素
if (begin1 <= end1)
{
memcpy(tmp + idx, arr + begin1, sizeof(int)*(end1 - begin1 + 1));
}
if (begin2 <= end2)
{
memcpy(tmp + idx, arr + begin2, sizeof(int)*(end2 - begin2 + 1));
}
//合并之后的序列拷到原始数组中
memcpy(arr + begin, tmp + begin, sizeof(int)*(end - begin + 1));
}
void MergeSort(int* arr, int begin, int end, int* tmp)
{
if (begin >= end)
return;
int mid = begin+(end - begin) / 2;
MergeSort(arr, begin, mid, tmp);
MergeSort(arr, mid + 1, end, tmp);
Merge(arr, begin, mid, end, tmp);
}
void MS(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
MergeSort(arr, 0, n - 1, tmp);
free(tmp);
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
MS(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
归并排序非递归法
//归并排序非递归
void Merge(int* arr, int begin, int mid, int end, int* tmp)
{
int begin1 = begin;
int end1 = mid;
int begin2 = end1 + 1;
int end2 = end;
//辅助空间的起始位置
int idx = begin;
//合并有序序列
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] <= arr[begin2])
{
tmp[idx] = arr[begin1];
idx++; begin1++;
}
else
{
tmp[idx++] = arr[begin2++];
}
}
//判断是否有未合并的元素
if (begin1 <= end1)
{
memcpy(tmp + idx, arr + begin1, sizeof(int)*(end1 - begin1 + 1));
}
if (begin2 <= end2)
{
memcpy(tmp + idx, arr + begin2, sizeof(int)*(end2 - begin2 + 1));
}
//合并之后的序列拷到原始数组中
memcpy(arr + begin, tmp + begin, sizeof(int)*(end - begin + 1));
}
void MergeNor(int *arr, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
int step = 1;
while (step < n)
{
for (int idx = 0; idx < n; idx += 2 * step)
{
//找到两个待合并的子序列区间:[begin,mid],[mid+1,end]
int begin = idx;
int mid = idx + step - 1;
//判断是否存在第二个子序列
if (mid >= n - 1)
//不存在,直接跳过
continue;
int end = idx + 2 * step - 1;
//判断第二个子序列是否越界
if (end >= n)
end = n - 1;
//合并
Merge(arr, begin, mid, end, tmp);
}
//更新步长
step *= 2;
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
MergeNor(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
非比较排序,如果range很大,会浪费很多空间
//非比较排序,时间复杂度o(Max(n,range)),空间复杂度o(range);
void CountSort(int* arr, int n)
{
int max, min;
min = 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* Countarr = (int*)calloc(range, sizeof(int));//创建数组的同时初始化为0
//计数
for (int i = 0; i < n; ++i)
{
Countarr[arr[i] - min]++;
}
//遍历
int idx = 0;
for (int i = 0; i < range; ++i)
{
while (Countarr[i]--)
{
arr[idx++] = i + min;
}
}
}
void main()
{
int arr[] = { 5,6,3,1,8,7,2,4 };
int n = sizeof(arr) / sizeof(arr[0]);
CountSort(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
}
下面对几种排序方法的性能进行比较
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<stdbool.h>
#include"queue.h"
//排序
//非比较排序
int getMid(int* arr, int left, int right)
{
int mid = left + (right + left) / 2;
if (arr[mid] > arr[left])
{
if (arr[mid] < arr[right])
{
return mid;
}
else if (arr[left] > arr[right])
{
return left;
}
else
{
return right;
}
}
else
{
if (arr[mid] > arr[right])
{
return mid;
}
else if (arr[left] < arr[right])
{
return left;
}
else
return right;
}
}
void CountSort(int* arr, int n)
{
int max, min;
min = 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* Countarr = (int*)calloc(range, sizeof(int));//创建数组的同时初始化为0
//计数
for (int i = 0; i < n; ++i)
{
Countarr[arr[i] - min]++;
}
//遍历
int idx = 0;
for (int i = 0; i < range; ++i)
{
while (Countarr[i]--)
{
arr[idx++] = i + min;
}
}
}
//归并排序非递归
void Merge(int* arr, int begin, int mid, int end, int* tmp)
{
int begin1 = begin;
int end1 = mid;
int begin2 = end1 + 1;
int end2 = end;
//辅助空间的起始位置
int idx = begin;
//合并有序序列
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] <= arr[begin2])
{
tmp[idx] = arr[begin1];
idx++; begin1++;
}
else
{
tmp[idx++] = arr[begin2++];
}
}
//判断是否有未合并的元素
if (begin1 <= end1)
{
memcpy(tmp + idx, arr + begin1, sizeof(int)*(end1 - begin1 + 1));
}
if (begin2 <= end2)
{
memcpy(tmp + idx, arr + begin2, sizeof(int)*(end2 - begin2 + 1));
}
//合并之后的序列拷到原始数组中
memcpy(arr + begin, tmp + begin, sizeof(int)*(end - begin + 1));
}
void MergeNor(int *arr, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
int step = 1;
while (step < n)
{
for (int idx = 0; idx < n; idx += 2 * step)
{
//找到两个待合并的子序列区间:[begin,mid],[mid+1,end]
int begin = idx;
int mid = idx + step - 1;
//判断是否存在第二个子序列
if (mid >= n - 1)
//不存在,直接跳过
continue;
int end = idx + 2 * step - 1;
//判断第二个子序列是否越界
if (end >= n)
end = n - 1;
//合并
Merge(arr, begin, mid, end, tmp);
}
//更新步长
step *= 2;
}
}
//归并排序递归法,时间复杂度o(N*logN),稳定。
void MergeSort(int* arr, int begin, int end, int* tmp)
{
if (begin >= end)
return;
int mid = begin+(end - begin) / 2;
MergeSort(arr, begin, mid, tmp);
MergeSort(arr, mid + 1, end, tmp);
Merge(arr, begin, mid, end, tmp);
}
void MS(int* arr, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
MergeSort(arr, 0, n - 1, tmp);
free(tmp);
}
//快速排序非递归法,用队列实现
int partQ(int* arr, int left, int right)
{
int mid = getMid(arr, left, right);
int tmp = arr[mid];
arr[mid] = arr[left];
arr[left] = tmp;
int key = arr[left];
while (left < right)
{
while (left < right&&arr[right] >= key)
{
right--;
}
arr[left] = arr[right];
while (left < right&&arr[left] <= key)
{
left++;
}
arr[right] = arr[left];
}
arr[left] = key;
return left;
}
void QuickSortnonrQ(int* arr, int n)
{
Queue q;
queueinit(&q);
queuePush(&q, 0);
queuePush(&q, n-1);
while (!queueempty(&q))
{
int left = queueFront(&q);
queuePop(&q);
int right = queueFront(&q);
queuePop(&q);
int div = partQ(arr, left, right);
if (left < div - 1)
{
queuePush(&q, left);
queuePush(&q, div-1);
}
if (div + 1 < right)
{
queuePush(&q, div+1);
queuePush(&q, right);
}
}
}
//快速排序前后指针法
int part3(int* arr, int left, int right)
{
int prev = left;//上一个小于基准值的位置
int cur = left+1;//下一个小于基准值的位置
int key = arr[left];
while (cur <= right)
//若cur位置的值大于基准值,则cur向后移动;
//否则,若cur和prev连续,则将两者同时向后移动继续进行cur位置与基准值的比较;
//若不连续,则将prev向后移动一位并与cur位置的值进行交换,再将cur向后移动
{
if (arr[cur] > key)
{
cur++;
}
else
{
if (cur == (prev + 1))
{
prev++;
cur++;
}
else
{
prev++;
int tmp = arr[prev];
arr[prev] = arr[cur];
arr[cur] = tmp;
cur++;
}
}
}
//当cur的位置移动到超出数组范围时,将基准值与prev位置的值进行交换;此时左边为小于基准值的值,右边为大于基准值的值。
int tmp = arr[left];
arr[left] = arr[prev];
arr[prev] = tmp;
return prev;
}
void partp(int* arr, int left, int right)
{
if (left >= right)
return;
int div = part3(arr, left, right);
partp(arr, left, div - 1);
partp(arr, div + 1, right);
}
//快速排序挖坑法
int part2(int* arr, int left, int right)
{
int mid = getMid(arr, left, right);
int tmp = arr[mid];
arr[mid] = arr[left];
arr[left] = tmp;
int key = arr[left];
while (left < right)
{
while (left < right&&arr[right] >= key)
{
right--;
}
arr[left] = arr[right];
while (left < right&&arr[left] <= key)
{
left++;
}
arr[right] = arr[left];
}
arr[left] = key;
return left;
}
void partw(int* arr, int left, int right)
{
if (left >= right)
return;
int div = part2(arr, left, right);
partw(arr, left, div - 1);
partw(arr, div + 1, right);
}
//快速排序优化,三数取中法确定基准值,起始位置,中间位置,结束位置;数据有序时,如果没有优化,可能会导致栈溢出,因为栈调用的层数约等于数据的个数,优化之后,划分比较均衡,时间复杂度为o(n*logn),栈的调用层数约等于logn;
//稳定性:不稳定
//快速排序递归实现,hoare版本
int parthoare(int* arr, int left, int right)
{
int mid = getMid(arr, left, right);//获取基准值的位置
//把基准值放到起始位置
int tmp = arr[left];
arr[left] = arr[mid];
arr[mid] = tmp;
int idx = arr[left];
int start = left;
while (left < right)
{
while (left < right&&arr[right] >= idx)//从后往前找第一个小于基准值的数据
{
right--;
}
while (left < right&&arr[left] <= idx)//从前往后找第一个大于基准值的数据
{
left++;
}
int tmp = arr[right];//交换两个数据
arr[right] = arr[left];
arr[left] = tmp;
}
int x = arr[start];//结束时将left和right相遇的位置的数据和基准值交换,此时基准值左边的值都小于基准值,右边的值都大于基准值;
arr[start] = arr[left];
arr[left] = x;
return left;//返回此时基准值所在的位置
}
void parth(int* arr, int left, int right)
{
if (left >= right)
return;
int div = parthoare(arr, left, right);
parth(arr, left, div - 1);//[left,div-1)
parth(arr, div + 1, right);//(div+1,right]
}
//冒泡排序,时间复杂度o(N^2),空间复杂度o(1),稳定。
void Bubble(int* arr, int n)
{
int end = n;
while (end > 1)//进行一次循环后,需要比较的数据就少一个。
{
int flag = 0;
for (int i = 0; i < end-1; i++)
{
if (arr[i] > arr[i + 1])
{
int tmp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = tmp;
flag = 1;
}
}
if(!flag)
break;
end--;
}
}
//堆排序,时间复杂度o(N*logN),空间复杂度o(1),不稳定
void shiftdown(int* arr, int n, int root)//若向下调整为最大堆,进行排序后数组数据排序方式为从小到大,若为最小堆,则排序方式为从大到小。
{
int child = 2 * root + 1;//左孩
while (child<n)
{
if (arr[child + 1] > arr[child] && child + 1 < n)
{
child++;//若右孩大于左孩且右孩没有超出数组范围,则将父节点与右孩比较;
}
if (arr[root] < arr[child])//调整为最大堆
{
int tmp = arr[root];//交换后继续从右孩处进行比较
arr[root] = arr[child];
arr[child] = tmp;
root = child;
child = 2 * root + 1;
}
else//若父节点比右孩大则结束循环
break;
}
}
void heap(int* arr, int n)
{
for (int i = (n - 2) / 2;i >= 0; --i)//从最后一个非叶子结点开始向上调整,调整到根结点;每次调整都将该非叶子结点的结构变为最大堆,结束循环后该数组即为最大堆
{
shiftdown(arr, n, i);
}
int end = n - 1;//进行排序,每次将最大值放到数组最后一个位置,并在下次排序时不将该最大值考虑在内
while (end > 0)
{
int tmp = arr[end];
arr[end] = arr[0];
arr[0] = tmp;
shiftdown(arr, end, 0);
--end;
}
}
//优化直接排序
void swap(int* arr, int pos1, int pos2)//交换函数
{
int tmp = arr[pos1];
arr[pos1] = arr[pos2];
arr[pos2] = tmp;
}
void Select(int* arr, int n)//优化后同时将最大值和最小值得到
{
int start = 0;
int end = n - 1;
while (start < end)
{
int min = start;
int max = start;
for (int i = start + 1; i <=end; i++)
{
if (arr[i] > arr[max])
{
max = i;
}
if (arr[i] < arr[min])
{
min = i;
}
}
swap(arr, start, min);//得到最小值后将最小值放在最前面
if (max == start)//若最大值就在第一个位置,则将其与最小值交换,否则会使最大值丢失
max = min;
swap(arr, end, max);//把最大值放在最后面
start++;//排除第一个和最后一个数据继续进行排序
end--;
}
}
//直接排序,时间复杂度o(N*N),空间复杂度o(1),不稳定。
void select(int* arr, int n)
{
int start = 0;
int end = n - 1;
while (start < end)
{
int min = start;
int i;
for (i=start+1; i < n; ++i)
{
if(arr[i] < arr[min])
{
min = i;
}
}
int tmp = arr[start];
arr[start] = arr[min];
arr[min] = tmp;
++start;
}
}
//希尔排序:时间复杂度o(N^1.3);不稳定
void shell(int* arr, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3+1;//定义每一次排序的两个数据之间的距离
for (int i = gap; i < n; i++)
{
int end = i - gap;//有序数列中的最后一个数据
int data = arr[i];
while (end >= 0 && arr[end] > data)//若最后一个数据大于同组的另一个数据,则将最后一个数据放在另一个数据之后,并更新end的值
{
arr[end + gap] = arr[end];
end = end - gap;
}
arr[end + gap] = data;//若最后一个数据小于同组数据,则将该数据插入到end+gap的位置
}
}
}
//插入排序:时间复杂度最大为o(N*N);最小为o(N);不稳定;
void insert(int* arr, int n)
{
for (int i = 1; i < n; ++i)
{
int end = i - 1;
//保存要插入的数据
int data = arr[i];
//如果插入的数据小于有序列的最后一个数,则将最后一个数向后移动;并让插入数据继续向前对比;
while (end >= 0 && arr[end] >= data)
{
arr[end + 1] = arr[end];
end--;
}
arr[end + 1] = data;
}
}
void main()
{
int n;
printf("数据量:\n");
scanf("%d", &n);
srand(time(NULL));
int* arr = (int*)malloc(sizeof(int)*n);
int* copy1 = (int*)malloc(sizeof(int)*n);
int* copy2 = (int*)malloc(sizeof(int)*n);
int* copy3 = (int*)malloc(sizeof(int)*n);
int* copy4 = (int*)malloc(sizeof(int)*n);
int* copy5 = (int*)malloc(sizeof(int)*n);
int* copy6 = (int*)malloc(sizeof(int)*n);
int* copy7 = (int*)malloc(sizeof(int)*n);
int* copy8 = (int*)malloc(sizeof(int)*n);
int* copy9 = (int*)malloc(sizeof(int)*n);
int* copy10 = (int*)malloc(sizeof(int)*n);
int* copy11= (int*)malloc(sizeof(int)*n);
int* copy12= (int*)malloc(sizeof(int)*n);
int* copy13= (int*)malloc(sizeof(int)*n);
for (int i = 0; i < n; ++i)
{
arr[i] = rand();
}
memcpy(copy1, arr, sizeof(int)*n);
memcpy(copy2, arr, sizeof(int)*n);
memcpy(copy3, arr, sizeof(int)*n);
memcpy(copy4, arr, sizeof(int)*n);
memcpy(copy5, arr, sizeof(int)*n);
memcpy(copy6, arr, sizeof(int)*n);
memcpy(copy7, arr, sizeof(int)*n);
memcpy(copy8, arr, sizeof(int)*n);
memcpy(copy9, arr, sizeof(int)*n);
memcpy(copy10, arr, sizeof(int)*n);
memcpy(copy11, arr, sizeof(int)*n);
memcpy(copy12, arr, sizeof(int)*n);
memcpy(copy13, arr, sizeof(int)*n);
time_t begin = clock();
insert(copy1, n);
time_t end = clock();
printf("插入排序:%d\n", end - begin);
begin = clock();
shell(copy2, n);
end = clock();
printf("希尔排序:%d\n", end - begin);
begin = clock();
select(copy3, n);
end = clock();
printf("直接排序:%d\n", end - begin);
begin = clock();
Select(copy4, n);
end = clock();
printf("优化直接排序:%d\n", end - begin);
begin = clock();
heap(copy5, n);
end = clock();
printf("堆排序:%d\n", end - begin);
begin = clock();
Bubble(copy6, n);
end = clock();
printf("冒泡排序:%d\n", end - begin);
begin = clock();
parth(copy7, 0,n-1);
end = clock();
printf("快速排序Hoare:%d\n", end - begin);
begin = clock();
partw(copy8, 0,n-1);
end = clock();
printf("快速排序挖坑:%d\n", end - begin);
begin = clock();
partw(copy9, 0, n - 1);
end = clock();
printf("快速排序指针:%d\n", end - begin);
begin = clock();
QuickSortnonrQ(copy10, n);
end = clock();
printf("非递归快速排序:%d\n", end - begin);
begin = clock();
MS(copy11, n);
end = clock();
printf("归并排序递归:%d\n", end - begin);
begin = clock();
MergeNor(copy12, n);
end = clock();
printf("归并排序非递归:%d\n", end - begin);
begin = clock();
CountSort(copy13, n);
end = clock();
printf("非比较:%d\n", end - begin);
}
结果为