排序算法
排序的基本概念:
排序:给定一组记录的集合{r1, r2, ……, rn},其相应的关键码分别为{k1, k2, ……, kn},排序是将这些记录排列成顺序为{rs1, rs2, ……, rsn}的一个序列,使得相应的关键码满足ks1≤ks2≤……≤ksn(称为升序)或ks1≥ks2≥……≥ksn(称为降序)。
正序:待排序序列中的记录已按关键码排好序。
逆序(反序):待排序序列中记录的排列顺序与排好序的顺序正好相反。
趟:在排序过程中,将待排序的记录序列扫描一遍称为一趟。通常,一次排序过程需要进行多趟扫描才能完成
排序算法的稳定性:
假定在待排序的记录集中,存在多个具有相同键值的记录,若经过排序,这些记录的相对次序仍然保持不变,即在原序列中,ki=kj且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
对于不稳定的排序算法,只要举出一个实例,即可说明它的不稳定性;而对于稳定的排序算法,必须对算法进行分析从而证明稳定的特性。
排序算法是否为稳定的是由具体算法决定的:
不稳定的算法在某种条件下可以变为稳定的算法,
而稳定的算法在某种条件下也可以变为不稳定的算法。
稳定性比较:
所有排序方法可分为两类,
(1)一类是稳定的,包括直接插入排序、起泡排序、和归并排序,基数桶式排序;
(2)另一类是不稳定的,包括直接选择排序、希尔排序、快速排序和堆排序。
排序的分类:
排序的分类——根据排序数据在内存中还是在外存中:
1.内排序:在排序的整个过程中,待排序的所有记录全部被放置在内存中
2. 外排序:由于待排序的记录个数太多,不能同时放置在内存,而需要将一部分记录放置在内存,另一部分记录放置在外存上,整个排序过程需要在内外存之间多次交换数据才能得到排序的结果。
排序的分类——根据排序过程中所进行的基本操作分类:
- 基于比较:基本操作——关键码的比较和记录的移动,其最差时间下限已经被证明为O(nlog2n)。
- 不基于比较:根据关键码的分布特征。比如,桶式排序,基数排序(多关键字排序)
基于比较的内排序: - 插入排序
- 交换排序
- 选择排序
- 归并排序
不基于比较的内排序: - 分配排序
2.桶式排序
3.基数排序
算法简单性比较:
从算法简单性看,
(1)一类是简单算法,包括直接插入排序、直接选择排序和起泡排序,
(2)另一类是改进后的算法,包括希尔排序、堆排序、快速排序、归并排序和基数排序,这些算法相对比较复杂。
直接插入排序
void insertSort (int r[ ], int n){
for (i=2; i<=n; i++) {
r[0]=r[i]; j=i-1;
while (r[0]<r[j]) {
r[j+1]=r[j];
j=j-1;
}
r[j+1]=r[0];
}
}
希尔排序
void Shellsort(int r[],int n){
for (d=n/2; d>=1; d=d/2){
for (i=d+1; i<=n; i++) {
r[0]=r[i];
j=i-d;
while (j>0 && r[0]<r[j])
{
r[j+d]=r[j];
j=j-d;
}
r[j+d]=r[0];
}
}
}
起泡排序
void BubbleSort(int r[ ], int n)
{
exchange=n;
while (exchange)
{
bound=exchange;
exchange=0;
for (j=1; j<bound; j++)
if (r[j]>r[j+1]) {
r[j]←→r[j+1];
exchange=j;
}
}
}
void BubbleSort(int r[ ], int n)
{
change=true; bound=n;
while (change)
{
change=false;
for (j=1; j<bound; j++)
if (r[j]>r[j+1]) {
r[j]←→r[j+1];
bound=j;
change=true
}
}
}
快速排序
int Partition(int r[ ], int first, int end)
{
i=first; j=end; //初始化
r[0]=r[i];
while (i<j)
{
while (i<j && r[0]<= r[j]) j--; //右侧扫描
if (i<j) {
r[i]=r[j]; i++; //将较小记录交换到前面
}
while (i<j && r[i]<= r[0]) i++; //左侧扫描
if (i<j) {
r[j]=r[i]; j--; //将较大记录交换到后面
}
}
r[i]=r[0];
retutn i; //i为轴值记录的最终位置
}
选择排序
void selectSort ( int r[ ], int n)
{
for ( i=1; i<n; i++)
{
index=i;
for (j=i+1; j<=n; j++)
if (r[j]<r[index]) index=j;
if (index!=i) r[i]<==>r[index];
}
}
堆调整
void sift ( int r[ ], int k, int m )
{//要筛选结点的编号为k,堆中最后一个结点的编号为m
i=k; j=2*i; temp=r[i]; //将筛选记录暂存
while (j<=m ) //筛选还没有进行到叶子
{
if (j<m && r[j]<r[j+1]) j++; //左右孩子中取较大者
if (temp>r[j]) break;
else {
r[i]=r[j]; i=j; j=2*i;
}
}
r[i]=temp; //将筛选记录移到正确位置
}
堆排序
void HeapSort ( int r[], int n)
{
for (i=n/2; i>=1; i--) //初建堆
sift(r, i, n) ;
for (i=1; i<n; i++ )
{
r[1]←→r[n-i+1]; //移走堆顶
sift(r, 1, n-i); //重建堆
}
}