第8章 排序技术
8.1 概述
在排序问题中,通常将数据元素成为记录。
若待排序序列中的记录已按关键码排好序,称此记录序列为正序;若待排序序列中记录的排列顺序与排好序的顺序正好相反,称此记录序列为逆序或反序。
排序的分类
内排序是指在排序的整个过程中,待排序的所有记录全部被放在内存中;外排序是指由于待排序的记录个数太多,不能同时放置在内存,而需要将一部分记录放置在内存,另一部分记录放置在外存。
可以将平排序方法分为基于比较的排序和不基于比较的排序。
8.2插入排序
8.2.1 直接插入排序
直接插入排序是插入排序中最简单的排序方法,类似于玩纸牌是整理手中纸牌的过程。
直接插入排序算法InsertSort
void InsertSort{int r[],int n}
{
for(i=2;i<=n;i++)
{
r[0]=r[i];
for(j=i-1;r[0]<r[j];j--)
r[j+1]=r[j];
r[j+1]=r[0];
}
}
8.2.2 希尔排序
希尔排序的基本思想是:先将整个待排序记录序列分割成若干个子序列,在子序列内分别进行直接插入排序,待整个序列基本有序时,在对全体记录进行一次直接插入排序。
希尔排序算法ShellSort
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];
for(j=i-d;j>0&&r[0]<r[j];j=j-d)
r[j+d]=r[0];
}
}
}
8.3 交换排序
8.3.1 起泡排序
起泡排序是交换排序中最简单的排序方法,其基本思想是两辆比较相邻记录的关键码,如果反序则交换,直到没有反序的记录为止。
起泡排序算法BubbleSort
void BubbleSort(int r[],int n)
{
exchange=n;
while (exchange!=0)
{
bound=exchange;exchange=0;
for(j=1;j<bound;j++)
if(r[j]>r[j+1]){
r[j]←→r[j+1];
exchange=j;
}
}
}
8.3.2 快速排序
快速排序是对起泡排序的一种改进,改进的着眼点是:在起泡排序中,记录的比较和移动是在相邻位置进行的,记录每次交换只能后移一个位置,因而总的比较次数和移动次数较多
选择轴值有多种方法,最简单的方法是选取第一个记录的关键码,还可以选取中间记录的关键码,或者在每次划分之前比较待排序序列的第一个记录,最后一个记录和中间记录的关键码,选取居中的关键码作为轴值并调换到第一个记录的位置。
一次划分的过程为
1 初始化
2 右侧扫描过程
3左侧扫描过程
4 重复2 3 步,直到i与j指向同一位置,即轴值记录的最终的位置。
快速排序一次划分算法Partition
int Partition(int r[],int first,int end)
{
i=first;j=end;
while(i<j)
{
while(i<j&&r[i]<=r[j])j--;
if(i<j){
r[i]←→r[j];
i++;
}
while (i<j&&r[i]<=r[j])i++;
if(i<j){
r[j]←→r[i];
j--;
}
}
return i;
}
快速排序算法QuickSort
void QuickSort(int r[],int first,int end)
{
if(first<end){
pivot=Patition(r,first,end);
QuickSort(r,first,pivot-1);
QuickSort(r,pivot+1,end);
}
}
8.4 选择排序
选择排序是一类借助“选择”进行排序的方法,其主要思想是:每趟排序在当前待排序序列中选出关键码最小的记录,添加到有序序列中。选择排序的特点是记录移动的次数较少。
8.4.1 简单选择排序
简单选择排序是选择排序中最简单的排序方法,其基本思想是:第i趟排序在待排序序列r[i]~r[n](1≤i≤n-1)中选取关键码最小的记录,并和第i个记录交换作为有序序列的第i个记录。
简单选择排序算法SelectSort
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];
}
}
8.4.2 堆排序
堆排序是简单选择排序的一种改进,改进的着眼点是:如何减少关键码的比较次数。堆排序再选出最小关键码的同时,也找出较小关键码,减少了在后面等选择中的比较次数,从而提高了整个排序的效率。
1、 堆的定义
堆是具有下列性质的完全二叉树:每个结点的值都小于或等于其左右孩子堆点的值(称为小根堆);或者每个结点的值都大于或等于其左右孩子堆点的值(称为大根堆)
从堆的定义可以看出,一个完全二叉树如果是堆,则根节点(称为堆顶)一定是当前堆中所有结点中的最大者或最小者。
筛选法调整堆的算法Sift
void Sift(int r[],int k,int m)
{
i=k;j=2*1;
while (j<=m)
{
if(j<m&&r[j]<r[j+1])j++;
if(r[i]>r[j])break;
else{
r[i]←→r[j];
i=j;j=2*i;
}
}
}
2、 堆排序
堆排序是利用堆的特性进行排序的方法,其基本思想是:首先将待排序的记录序列构造成一个堆,此时,选出了堆中所有记录的最大者即堆顶记录。然后将堆顶记录移走,并将剩余的记录再调整成堆,这样又找出了次大的记录。以此类推,直到堆中只有一个记录为止
堆排序算法HearSort
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,i,n-i);
}
}
8.5 归并排序
8.5.1 二路归并排序的非递归实现。
具体的排序过程是:将具有n个待排序的记录序列看成是n个长度为1的有序序列,然后进行两辆归并,得到[n/2]个长度为2(最后一个有序序列的长度可能是1)的有序序列,再进行两辆归并,得到[n/4]个长度为4的有序序列(最后一个有序序列的长度可能小于4),……直至得到一个长度为n的有序序列。
一次病归算法 Merge
void Merge(int r[],int r1[],int s,int m,int t)
{
i=s;j=m+1;k=s;
while (i<=m&&j<=t)
{
if(r[i]<=r[j]) r1[k++]=r[i++];
else r1[k++]=r[j++];
}
if(i<=m)while (i<=m0
r1[k++]=r[i++];
else while (j<=t)
r1[k++]=r[j++];
}
一趟归并排序算法MergePass
void MergePass(int r[],int r1[],int n,int h)
{
i=1;
while (i<=n-2h+1)
{
Merge(r,r1,i,i+h-1,i+2*h-1);
i+=2*h;
}
if(i<n-h+1)Merge(r,r1,i,i+h-1,n);
else for(k=i;k<=n;k++)
r1[k]=r[k];
}
归并排序非递归算法MergeSort
void MergeSort1(int r[],int r1[],int n)
{
h=1;
while(h<n)
{
MergeSort(r,r1,n,h);
h=2*h;
MergePass(r1,r,n,h);
h=2*h;
}
}
8.5.2 二路归并排序的递归实现
二路并归排序方法也可以用递归的形式描述,即首先将待排序的记录序列分为两个相等的子序列,并分别将这两个子序列用并归方法进行排序,然后调用一次并归算法Merge,再将这两个有序子序列合并成一个含有全部记录的有序序列。
归并排序的递归算法MergeSort2
void MergeSort2(int r[],int r1[],int s,int t)
{
if(s==t)r1[s]=r[s];
else{
m=(s+t)/2;
Mergesort2(r,r1,s,m);
Mergesort2(r,ri,m+1,t);
Merge(r1,r,s,m,t);
}
}