选择排序
选择排序的基本思想:每一次在n-i+1(i=1,2,…,n-1)个记录中选取键值最小的记录作为有序序列的第i个记录。
直接选择排序
直接选择排序算法的基本思想:在第i次选择操作中,通过n-i次键值间比较,从n-i+1个记录中选出键值最小的记录,并和第i(1≤i≤n-1)个记录交换。
算法描述如下:
void selectSort(List R,int n)
{
int min,i,j;
for(i=1;i<=n-1;i++)
{
min=i;
for(j=i+1;j<=n;j++)
{
if(R[j].key<R[min].key)
{
min=j;
}
if(min !=i)
{
swap(R[min],R[i]);
}
}
}
}
直接选择排序算法简单,容易实现,但不适宜于n较大的情况。时间复杂度为O(n²)。
堆排序
若有一个关键字序列{k1,k2…,kn} 满足 k(i)≤k(2i),k(i)≤k(2i+1)或k(i)≥k(2i),k(i)≥k(2i+1),其中,i=1,2,…, ⌊n/2⌋,则称这个n个键值的序列{k(1),k(2),…k(n)}为最小堆(或最大堆)。
算法如下:
void HeapSort(List R)
//对R[n]进行堆排序,排序完成后,R中记录按关键字自大至小有序排列
{
int i;
for(i=n/2;i>=1;i--)
{
Shit(R,i,n); //从第n/2个记录开始进行筛选建堆
}
for(i=n;i>=2;i--)
{
swap(R[1],R[i]); //将堆顶记录和堆中最后一个记录互换
Sift(R,1,i-1); //调整R[1]使R[1],…,R[i-1]变成堆
}
}
void Sift(List R,int k,int m)
{
int i,j,x;
List t;
i=k;j=2*i;
x=R[k].key;
t=R[k];
while(j<=m)
{
if((j<m) && (R[j].key>R[j+1].key))
{
j++; //若存在右子树,且右字树根的关键字小,则沿右分支筛选
}
if(x<R[j].key)
{
break; //筛选完毕
}
else
{
R[i]=R[j];
i=j;
j=2*i;
}
}
R[i]=t; //填入恰当位置
}
堆排序在待排序记录较少时不适用,但对记录数很多时是很有效的,因为其主要运行时间耗费在建初始堆和不断“筛选”的过程。对于n个记录进行排序所需的平均时间时O(nlog(2)n)。在最坏情况下,其时间复杂度也为O(nlog(2)n)。相对于快速排序来说,这是堆排序的最大优点。此外,堆排序仅需一个记录大小的供交换用的辅助存储空间。
堆排序是不稳定的。归并排序
归并排序是与插入排序、交换排序,选择排序不同的一类排序方法,其不同之处在于要求待排序列是由若干个子序列组成。归并的含义是将两个或两个以上的有序表合并成一个新的有序表。合并的方法是比较各子序列的第一个记录的键值,最小的一个就是排序后序列的第一个记录的键值。取出这个记录,继续比较各自序列现有的第一个记录的键值,便可中出排序后的第二个记录。如此继续下去,最终可以得到排序结果。因此归并排序的基础是合并。
有序序列的合并
void Merge(List a,List R,int h,int m,int n)
//将ah,…am和am+1,…,an两个有序序列合并成一个有序序列Rh,…Rn
{
k=h,j=m+1; //k,j置成文件的起始位置
while((h<=m) && (j<=n)) //将a中记录从小到大合并入R
{
if(a[h].key<=a[j].key) //a[h]键值小,送入R[k]并修改h值
{
R[k]=a[h];
h++;
}
else //a[j]键值小,送入[k]并修改j值
{
R[k]=a[j];
j++;
}
k++;
}
while(h<=m) //j>n,将ah,…am剩余部分插入R的末尾
{
R[k]=a[h];
h++;
k++;
}
while(j<=n) //h>m,将am+1,…,an剩余部分插入R的末尾
{
R[k]=a[j];
j++;
k++;
}
}
此算法的执行事件为O(n-h+1)。
二路归并排序
void MergePass(List a,List b,int n,int h)
{
i=1;
while(i<=n-2*h+1)
{
Merge(a,b,i+h-1,i+2*h-1);
i+=2*h;
}
if(i+h-1<n)
{
Merge(a,b,i,I=h-1,n);
}
else
{
for(t=i;t<=n;t++)
b[t]=a[t];
}
}
void MergeSort(List a,int n) //将序列a1,a2,…an按关键字的非递减次序进行排序,b也定义为list类型
{
m=1; //m为子序列长度,初始值为1
while(m<n)
{
MergePass(a,b,n,m); //将序列a中有序子序列合并到b
m=2*m; //子序列长度扩大1倍
MergePass(b,a,n,m); //将序列b中有序子序列合并到a
m=2*m; //子序列长度扩大1倍
}
}
在n较大时,归并排序的时间性能优于堆排序,但它所需的辅助存储量较多。二路归并排序是稳定的。