下列是十种排序的实现:
1.插入排序
void InsertSort(int a[], int n)
{
int i, j;
for (i = 1; i < n; ++i)
{
int t = a[i];//暂存待插码
for (j = i - 1; t < a[j] && j >= 0; --j)//寻找插入位置
a[j + 1] = a[j];//后移
a[j + 1] = t;
}
}
2.希尔排序
在插入排序基础上改进,将整个排序的序列分割为若干子序列,子序列内部排序插入排序,待整个序列基本有序时对全体插入排序。
使用的分割方法是取d<n,所有相距为d的码构成一个子序列,插入排序,然后缩小d再进行排序,直到d=1,再将全体进行插入排序。可以令d=n/2,然后每次缩小d时,令d=d/2。
void ShellSort(int a[], int n)
{
int d, i, j;
for (d = n / 2; d >= 1; d = d / 2)
{
for (i = d + 1; i <= n; ++i)
{
a[0] = a[i];
for (j = i - d; a[0] < a[j]; j -= d)
a[j + d] = a[j];
a[j + d] = a[0];
}
}
}
3.冒泡排序
void BubbleSort(int a[], int n)
{
int i, j;
for (i = 0; i < n; ++i)
for (j = 0; j < n - i-1;++j)
if (a[j]>a[j+1])
{
int t=a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
4.快速排序
以一个码为轴,将序列化为两部分,左边小于等于轴值,右边大于等于轴值,重复操作直到序列有序。
下面代码用的选取轴值策略是取待划分部分的末尾为轴,小于等于它的元素则按左到右的顺序依次与其左侧元素交换,最后再将轴值交换到最后一次交换位置的下一位置。
而取轴值位置将关系到算法的性能
int Partition(int a[], int p, int r)//取轴值并交换
{
int i = p - 1, j;
for (j = p; j < r;++j)
if (a[j]<=a[r])
{
++i;
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int t = a[i + 1];
a[i + 1] = a[r];
a[r] = t;
return i + 1;
}
void QuickSort(int a[], int p, int r)//初始QuickSort(a, 0, n-1)
{
if (p < r)
{
int q = Partition(a,p,r);
QuickSort(a,p,q-1);
QuickSort(a,q+1,r);
}
}
5.选择排序
void SelectSort(int a[], int n)
{
int i, j, k;
for (i = 0; i < n-1; ++i)
{
k = i;
for (j = i+1; j < n; ++j)
if (a[j] < a[k])
k = j;
if (k != i)
{
int t = a[j];
a[j] = a[i];
a[i] = t;
}
}
}
6.堆排序
堆为一棵完全二叉树,子女比自己小者为最大堆,子女比自己大者为最小堆。因此,最大堆
的根结点为最大元素,最小堆的根结点为最小元素,堆排序就是利用这一点,每次将堆的根
结点移除,重新调整堆,如此重复直到堆为空,这样堆中元素即按从大到小从堆中取出。
#define PARENT(i) i/2//当前结点父结点下标
#define LEFT(i) 2*i//当前结点左孩子下标
#define RIGHT(i) (2*i+1)//当前结点右孩子下标
void MaxHeapify(int a[], int k, int i)//保持最大堆性质,k是堆的长度,a下标要从1开始
{
int l=LEFT(i), r=RIGHT(i);
int largest=0;
if (l <= k&&a[l] > a[i])
largest = l;
else
largest = i;
if (r <= k&&a[r] > a[largest])
largest = r;
if (largest != i)
{
int t = a[i];
a[i] = a[largest];
a[largest] = t;
MaxHeapify(a, k, largest);
}
}
void BuildMaxHeap(int a[],int n)//初始化最大堆
{
for (int i = n / 2; i > 0; --i)
MaxHeapify(a, n, i);
}
void HeapSort(int a[], int n)//堆排序,a[0]作为交换中间量
{
BuildMaxHeap(a, n);
int k = n;
for (int i = n ; i >= 2; --i)
{
a[0] = a[1];
a[1] = a[i];
a[i] = a[0];
--k;
MaxHeapify(a, k, 1);
}
}
7.归并排序
将两堆已排好序的元素合并,合并过程就是一一比较两个序列中的元素,按大小顺序重新排
成一个序列,因为两个序列均为已排好序的序列,所以合并两个总长为n的序列的过程只耗
费O(n),然后重复合并过程,直到整个序列有序。
void Merge(int a[], int x, int y, int z)//合并a[p...q]与a[q+1...r],假设其已经排好序
{
int n1 = x - y + 1, n2 = z - y;
int *l = new int[n1];
int *r = new int[n2];
for (int i = 0; i < n1; ++i)
l[i] = a[x + i];
for (int i = 0; i < n2; ++i)
r[i] = a[y + i + 1];
l[n1] = INF;//设置哨兵
r[n2] = INF;
int i = 0, j = 0;
for (int k = x; k <= z; ++k)
{
if (l[i] < r[j])
{
a[k] = l[i];
++i;
}
else
{
a[k] = r[j];
++j;
}
}
}
void MergeSort(int a[], int x, int z)//初始参数MergeSort(a, 0, n)
{
if (x < z)
{
int y = (x + z) / 2;
MergeSort(a, x, y);
MergeSort(a, y, z);
Merge(a, x, y, z);
}
}
8.计数排序
计数排序假设n个输入元素中的每一个均是0到k之间的数,k某整数,用一中间暂存数组存储
每个数的个数(即用每个待排数作为数组下标)来作为该数在结果数组中的位置依据,这样
序列中重合的元素也能得到很好的处理。计数排序因为并没有用比较的方法排序,所有计数
排序下界优于基于比较的排序下界O(nlogn),但要使用大量额外的空间去暂存中间数据。
void CountSort(int a[], int b[], int n, int k)//a[]为待排序列,b[]存储结果
{
int *c = new int[k+1];
for (int i = 0; i <= k; ++i)//初始化暂存每个数个数的数组,下标为序列中的数。
c[i] = 0;
for (int i = 0; i < n; ++i)//每个单元存储的内容为该数个数。
++c[a[i]];
for (int i = 1; i <= k; ++i)//每个单元存储小于或等于该数的元素个数,以此作为该数在序列中的位置
c[i] += c[i - 1];
for (int i = n - 1; i >= 0; --i)
{
b[c[a[i]]] = a[i];
--c[a[i]];
}
}
9.基数排序
基数排序利了多个关键码排序的思想对单关键码排序,将单关键码按位排序,按位排序可使
用任意排序方法,与直觉相反的是基数排序是按从低位到高位的次序排序,这样避免了从高
位到低位排序时产生的中间状态不参与排序的数,n个k进制d位数,若使用计数排序,则时间
复杂度为O(d(n+k))
void CountSort(int a[], int b[], int n, int k,int d)//用于基数排序,按a[]的第d位排序
{
int *c = new int[k+1];
for (int i = 0; i <= k; ++i)
c[i] = 0;
for (int i = 0; i < n; ++i)
{
int t=a[i];
int temp = 1;
for (int j = 0; j < d; ++j)
temp *= 10;
t = (t / temp)%10;
++c[t];
}
for (int i = 1; i <= k; ++i)
c[i] += c[i - 1];
for (int i = n - 1; i >= 0; --i)
{
int t = a[i];
int temp = 1;
for (int j = 0; j < d; ++j)
temp *= 10;
t = (t / temp) % 10;
b[c[t]] = a[i];
--c[t];
}
}
void RadixSort(int a[], int n, int d)
{
int *b = new int[n];
for (int i = 0; i < d; ++i)
{
//用一种稳定算法对序列按第d位排序,可以使用计数排序
CountSort(a, b, n, 9, d);//对十进制序列按第d位计数排序
for (int i = 0; i < n; ++i)
a[i] = b[i];
}
}
10.桶排序
桶排序的思想就是将序列元素的区间划为x等分,每一等分即为一个桶,对每个桶中的元素进行排序,一般用链表,所以用插入排序,最后把所有元素从桶中取出即排好序。桶排序需要用
到额外存放链表(即桶)的数组,并且假设序列是均匀分布的,所有,输入序列趋于均匀分
布时,桶排序趋于线性期望时间