排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。分内部排序和外部排序。若整个排序过程不需
要访问外存便能完成,则称此类排序问题为内部排序。反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问
题为外部排序。内部排序的过程是一个逐步扩大记录的有序序列长度的过程。
我们这里说的是内部排序。
勘误 : 选择排序是不稳定的 !!!
/*
*
* 各类排序算法复习(简单数组实现,其他类似)
* Sort Type Review
* 2015.11.09
* By Snow Yong
*
*/
#define MINIMUMSIZE 7 //快速排序自定义小数组数目
#define A_LENGTH 10 //数组长度
#include <stdio.h>
int Swap(int *a, int i, int j);
int BubbleSort(int *a);
int SelectSort(int *a);
int InsertSort(int *a);
int ShellSort(int *a);
int HeapSort(int *a);
int HeapAdjust(int *a, int low, int high);
int MergeSort(int *a);
int Msort(int *S, int *T, int low, int high);
int Merge(int *S, int *T, int low, int m, int high);
int QuickSort(int *a);
int Qsort(int *a, int low, int high);
int Partition(int *a, int low, int high);
int PickMiddle(int *a, int low, int high);
/* Swap Function using to swap element
* 元素交换函数
*/
int Swap(int *a, int i, int j)
{
int temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
//BubbleSort -- 冒泡排序 O(n^2)
int BubbleSort(int *a)
{
/*
* flag is using for sort flag
* flag变量用于提高冒泡效率
*/
int i, j, flag = 1;
for (i = 0; i < A_LENGTH-1 && flag; i++)
{
flag = 0;//若flag经 j 循环后为0,则说明序列已然有序
for (j = A_LENGTH-1; j > i; j--)
{
if (a[j] < a[j-1])
{
Swap(a, j, j-1);
flag = 1;
}
}
}
}
//SelectSort -- 选择排序 O(n^2)
int SelectSort(int *a)
{
int i, j, min;
for (i = 0; i < A_LENGTH-1; i++)
{
min = i;
for (j = i+1; j < A_LENGTH; j++)
{
if (a[min] > a[j])
{
min = j;/*不直接交换值,而是记录下标*/
}
}
//若下标不同再一次替换成最小值
if (min != i)
{
Swap(a, min, i);
}
}
}
//InsertSort -- 插入排序 O(n^2)
int InsertSort(int *a)
{
int i, j, temp;
//从第二个数开始循环
for (i = 1; i < A_LENGTH; i++)
{
//满足前提的条件下再记录a[i]的值
if (a[i] < a[i-1])
{
temp = a[i];
for (j = i-1;
a[j] > temp && j >= 0;
j--)
{
a[j+1] = a[j];
}
a[j+1] = temp;/*此处为a[j+1]而不是a[j]是因为上面循环最后执行了j--*/
}
}
}
//ShellSort -- 希尔排序 O(n^(3/2))
int ShellSort(int *a)
{
int i, j, temp;
int increament = A_LENGTH;/*类似插入排序,increament增量处理*/
//跳跃式插入排序
do
{
increament = increament/3 + 1;
for (i = 0+increament; i < A_LENGTH; i++)
{
if (a[i] < a[i-increament])
{
temp = a[i];
for (j = i-increament;
a[j] > temp && j >= 0;
j -= increament)
{
a[j+increament] = a[j];
}
a[j+increament] = temp;
}
}
}
while (increament > 1);
}
//HeapSort -- 堆排序 O(nlogn)
int HeapSort(int *a)
{
int i;
//自行查阅堆和大(小)顶堆的定义
//初始化从中间位置开始构造大顶堆
for (i = A_LENGTH/2 - 1; i >= 0; i--)
{
HeapAdjust(a, i, A_LENGTH-1);
}
//此处一边将最大值选出,一边重新从 0 到 i-1 下标调整大顶堆
for (i = A_LENGTH - 1; i > 0; i--)
{
Swap(a, 0, i);
HeapAdjust(a, 0, i-1);
}
}
//该函数为堆的调整函数(将序列调整为大顶堆), 核心实现函数
int HeapAdjust(int *a, int low, int high)
{
int i, j, temp = a[low];
for (i = 2*low + 1; i <= high; i = 2*i + 1)
{
if (i < high && a[i] < a[i+1])
{
i++;
}/*左右孩子值比较取其较大者*/
if (temp >= a[i])
{//父节点值不小于孩子节点值
break;
}
a[low] = a[i];
low = i;/*不断将下标替换较大值的孩子的下标*/
}
a[low] = temp;
}/*此函数用于调整排序序列为大顶堆*/
//MergeSort -- 归并排序(递归式) O(nlogn),
//非递归的归并算法请参考博文 ”非递归 -- 归并排序(C语言)“ ,注:一般推荐采用非递归的归并算法
int MergeSort(int *a)
{
Msort(a, a, 0, A_LENGTH-1);
}
int Msort(int *S, int *T, int low, int high)
{
int m;
int T_temp[A_LENGTH];/*辅助数组*/
if (S[low] == S[high])
{
T[low] = S[low];
}
else
{
m = (low + high) / 2;
Msort(S, T_temp, low, m);
Msort(S, T_temp, m+1, high);
Merge(T_temp, T, low, m, high);
}
}
int Merge(int *S, int *T, int low, int m, int high)//归并主要实现函数
{
int j, k, l;/*k用于T数组的下标前进*/
for (k = low, j = m+1;
low <= m && j <= high;
k++)
{
if (S[low] < S[j])
{
T[k] = S[low++];
}
else
{
T[k] = S[j++];
}
}
if (low <= m)
{
for (l = 0; l <= m-low; l++)
{
T[k+l] = S[low+l];
}
}
if (j <= high)
{
for (l = 0; l <= high-j; l++)
{
T[k+l] = S[j+l];
}
}
}
//QuickSort -- 快速排序(已优化) O(nlogn)
int QuickSort(int *a)
{
Qsort(a, 0, A_LENGTH-1);
}
int Qsort(int *a, int low, int high)
{
int pivot;/*枢轴值*/
if ((high - low) > MINIMUMSIZE)
{
while (low < high)
{
pivot = Partition(a, low, high);
Qsort(a, low, pivot-1);
low = pivot + 1;/*此处优化Qsort(a, pivot+1, high)的尾递归*/
}
}
else/*优化小数组的处理方式 -- 变为插入排序效率更高*/
{
InsertSort(a);
}
}
//此处为求枢轴的函数
int Partition(int *a, int low, int high)
{
int temp;
PickMiddle(a, low, high);/*经此函数保证a[low]为较接近中间值*/
temp = a[low];
while (low < high)
{
while (low < high && a[high] >= temp)
{
high--;
}
a[low] = a[high];/*此处优化不必要的交换*/
while (low < high && a[low] <= temp)
{
low++;
}
a[high] = a[low];/*此处优化不必要的交换*/
}
a[low] = temp;
return low;
}
//取接近序列中间大小值并交换给a[low]的函数
int PickMiddle(int *a, int low, int high)
{
int m = (low + high) / 2;
if (a[low] > a[high])
{
Swap(a, low, high);
}
if (a[m] > a[high])
{
Swap(a, m, high);
}
if (a[low] > a[m])
{
Swap(a, low, m);
}
}
int main()
{
int i;
int a[A_LENGTH] = {50, 90, 80, 40, 30,
70, 60, 10, 20, 100};
printf("Before sorting:");
for (i = 0; i < A_LENGTH; i++)
{
printf("%d -- ", a[i]);
}
// BubbleSort(a);
// SelectSort(a);
// InsertSort(a);
// ShellSort(a);
// HeapSort(a);
// MergeSort(a);
QuickSort(a);
printf("\n\nAfter sorting: ");
for (i = 0; i < A_LENGTH; i++)
{
printf("%d -- ", a[i]);
}
return 0;
}
运行结果就不上图了,跟其他排序详解都差不多,因为不喜欢VS卡又慢,所以平台是win + notepad++,编译用的是 MinGW 的 gcc ,因为懒得开虚拟机运行CentOS,
大概结果可参考其他排序算法详解,能力有限,谢谢阅读!
当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;