排序(Sort),就是重新排序表中的元素,使表中的元素满足按关键字允许的过程
排序算法的评价指标
时间复杂度、空间复杂度
算法的稳定性:
插入排序
算法思想:每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中,直到全部记录插入完成
算法实现
//直接插入排序
void InserSort(int A[],int n){
int i,j,temp;
for(i=1;i<n;i++) //将各元素插入已排好序的序列中
if(A[i]<A[i-1]){ //若A[i]关键字小于前驱
temp=A[i]; //用temp暂存A[i]
for(j=i-1;j>=0 && A[j]>temp;--j) //检查所有前面已排好序的元素
A[j+1]=A[j]; //所有大于temp的元素都向后挪位
A[j+1]=temp; //复制到插入位置
}
}
算法实现(带哨兵)
算法效率分析
空间复杂度:O(1)
时间复杂度:主要来自对比关键字、移动元素若有n个元素,则需要n-1趟处理
最好情况:
共n-1趟处理,每一趟只需要对比关键字1次,不用移动元素
最好时间复杂度--O(n)
最坏情况:
最坏时间复杂度--O(n^2)
优化--折半插入排序
思路:先用折半查找找到应该插入的位置,再移动元素
//折半插入排序
void InserSort(int A[],int n){
int i,j,low,high,mid;
for(i=2;i<=n;i++){
A[0]=A[i];
low=1;high=i-1;
while(low<=high){
mid=(low+high)/2;
if(A[mid]>A[0]) high=mid-1;
else low=mid+1;
}
for(j=i-1;j>=high+1;--j)
A[j+1]=A[j];
A[high+1]=A[0];
}
}
希尔排序(Shell Sort)
先追求表中元素部分有序,再逐渐逼近全局有序
算法实现
//希尔排序
void Shell Sort(int A[],int n){
int d,i,j;
for(d=n/2;d>=1;d=d/2)
for(i=d+1;i<=n;++i)
if(A[i]<A[i-d]){
A[0]=A[i];
for(j=i-d;j>0&&A[0]<A[j];j-=d)
A[j+d]=A[j];
A[j+d]=A[0];
}
}
冒泡排序
基于“交换”的排序:根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置
、
算法实现
//交换
void swap(int &a,int &b){
int temp=a;
a=b;
b=temp;
}
//冒泡排序
void BubbleSort(int A[],int n){
for(int i=0;i<n-1;i++){
bool flag=false;
for(int j=n-1;j>i;j--)
if(A[j-1]>A[j]){
swap(A[j-1],A[j]);
flag=true;
}
if(flag==false)
return;
}
}
算法性能分析
快速排序
基于“交换”的排序:根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置
int Partition(int A[],int low,int high){
int pivot=A[low];
while(low<high){
while(low<high&&A[high]>=pivot)--high;
A[low]=A[high];
while(low<high&&A[low]<=pivot)++low;
A[high]=A[low];
}
A[low]=pivot;
return low;
}
void QuickSort(int A[],int low,int high){
if(low<high){
int pivotpos=Partition(A,low,high);
QuickSort(A,low,pivotpos-1);
QUickSort(A,pivotpos+1,high);
}
}
算法效率分析
最坏情况
比较好的情况
若每一次选中的“枢轴”将待排序序列划分为均匀的两个部分,则递归速度最小,算法效率最高
简单选择排序
选择排序:简单选择排序、堆排序 选择排序:每一趟在待排序元素中选取关键字最小(或最大)的元素加入有序子序列
每一趟在待排序元素中选取关键字最小的元素加入有序子序列
算法实现
void SelectSort(int A[],int n){
for(int i=0;i<n-1;i++){
int min=i;
for(int j=i+1;j<n;j++)
if(A[j]<A[min]) min=j;
if(min!=i) swap(A[i],A[min]);
}
}
void swap(int &a.int &b){
int temp=a;
a=b;
b=temp;
}
算法性能分析
堆排序
什么是堆
建立大根堆
void BuildMaxHeap(int A[],int len){
for(int i=len/2;i>0;i--)
HeadAdjust(A,i,len);
}
void HeadAdjust(int A[],int k,int len){
A[0]=A[k];
for(int i=2*k;i<=len;i*=2){
if(i<len&&A[i]<A[i+1])
i++;
if(A[0]>=A[i])break;
else{
A[k]=A[i];
k=i;
}
}
A[k]=A[0];
}
基于大根堆进行排序
void BuildMaxHeap(int A[],int len)
void HeadAdjust(int A[],int k,int len)
void HeapSort(int A[],int len){
BuildMaxHeap(A,len);
for(int i=len;i>1;i--){
swap(A[i],A[1]);
HeadAdjust(A,1,i-1)
}
}
算法效率分析
在堆中插入新元素
在堆中删除元素
归并排序
归并:把两个或多个已经有序的序列合并成一个
代码实现
int *B=(int *)malloc(n*sizeof(int));
void Merge(int A[],int low,int mid,int high){
int i,j,k;
for(k=low;k<=high;k++)
B[k]=A[k];
for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){
if(B[i]<=B[j])
A[k]=B[i++];
else
A[k]=B[j++];
}
while(i<=mid) A[k++]=B[i++];
while(j<=high) A[k++]=B[j++];
}
void MergeSort(int A[],int low,int high){
if(low<high){
int mid=(low+high)/2;
MergeSort(A,low,mid);
MergeSort(A,mid+1,high);
Merge(A,low,mid,high);
}
}
基数排序(Radix Sort)
算法效率分析
稳定性
基数排序的应用
外部排序
外存、内存之间的数据交换
外部排序原理
构造初始“归并段”
第一趟归并
把8个有序子序列(初始归并段)两两归并
第二趟归并
把4个有序子序列(归并段)两两归并
第三趟归并
把2个有序子序列(归并段)归并
时间开销分析
外部排序时间开销=读写外存的时间+内部排序所需时间+内部归并所需时间
优化:多路归并
败者树
多路平衡归并带来的问题
什么是败者树
基于已经构建好的败者树,选出新的胜者只需进行3场比赛
败者树在多路平衡归并中的应用
置换-选择排序
最佳归并树
归并树的神秘性质
构造2路归并的最佳归并树
多路归并的情况
多路归并的最佳归并树
如果减少一个归并段
正确的做法