#选择排序
##简单选择排序
比如从小到大排序的话,从未排序序列中选出最小的和序列的首元素交换,然后再在剩下的未排序序列中选出最小的和序列的第二个元素交换,以此类推,最后形成从小到大的已排序序列。C语言代码解释如下:
void SimpleSelectionSort(ElementType A[],int N){
int i,j,min,temp;
for(i=0;i<N-1,i++){
min=i;
for(j=i+1,j<N;j++){
if(A[min]>A[j]){
//确定较小元素的位置
min=j;
}
}
//交换元素的值
temp=A[i];
A[i]=A[min];
A[min]=temp;
}
}
##堆排序
字面意思就是用堆这种数据结构所设计的排序算法。堆是一种特殊的二叉树,每个子节点的值总是大于或小于他的父节点,相应的分为最小堆和最大堆。C语言解释代码:
void Ajust(ElementType A[],int i,int N){
int child;
Element temp;
//将当前元素的节点和其子节点比较,将最大值放到父节点上
for(temp=A[i];(2*i+1)<N;i=child){
child=2*i+1;
if((child!=N-1)&&(A[child+1]>A[child])){
child++;
}
if(temp<A[child]){
A[i]=A[child];
}
else break;
}
A[i]=temp;//将temp放到当前位置
}
void heapSort(ElementType A[],int N){
int i;
ElementType temp;
//建立最大推
for(i=(N-1)/2;i>=0;i++){
Ajust(A,i,N);
}
for(i=N-1;i>0;i--){
//将堆顶元素与当前堆的最后一个元素i互换
temp=A[0];A[0]=A[i];A[i]=temp;
//重新建立最大堆
Ajust(A,0,i);
}
}
#插入排序
##简单插入排序
核心思想是将待排序的序列分为已排序序列和未排序序列。比如一个未排序序列,直接默认已排序序列只有第一个元素这种就行。C语言解释代码:
void InsertionSort(ElementType A[],int N){
int i,j;
ElementType temp;
for(i=0;i<N;i++){
temp=A[i];
for(j=i;(j<0)&&(temp<A[j-1]);j--){
A[j]=A[j-1];//将已排序元素大于当前temp的元素右移
}
A[j]=temp;//将当前的temp元素放入合适位置
}
}
##希尔排序
希尔排序是每次交换间隔一定距离的元素(比如1,3,6,4,5;取其中的1和4,就相当于间隔3;类似的直到间隔为1则完成了最终排序),C语言解释代码:
void ShellSort(ElementType A[],int N,int Incre[],int M){
//Incre[]为排序增量的数组,最后一个元素的值为1(说明排序完成)
int i,j;k,Increment;
ElementType temp;
for(i=0;i<M;i++){
Increment=Incre[i];//选择排序的增量
//进行一次增量排序
for(j=Increment;j<N;j++){
temp=A[j];
for(k=j;k-Increment>=0;k-=Increment){
if(temp>=A[k-Increment])break;
else
A[k]=A[k-Increment];
A[k]=temp;
}
}
}
}
#交换排序
##冒泡排序
最简单的交换排序,通过不断循环将最大或者最小元素交换出来,放到未排序元素的最后。C语言解释代码:
void BubbleSort(ElementType A[],int N){
int i,j;
bool flag;
ElementType temp;
for(i=N-1;i>=0;i--){
//初始时标记未交换
flag=0;
//循环找出最大元素放到最右端
for(j=0;i<i;j++){
if(A[j]>A[j+1]){
temp=A[j];
A[j]=A[j+1];
A[j+1]=temp;
//标记是否交换
flag=1;
}
}
if(!flag)break;//如果一次未发生交换说明有序,跳出循环
}
}
##快速排序
原理是将未排序的元素根据基准分为两个子序列,一个子序列均大于基准,另一个均小于基准,然后递归的对这两个子序列用类似方法排序。C语言解释代码:
//交换两个元素的值
void Swap(ElementType *a,ElementType *b){
ElementType temp=*a;
*a=*b;
*b=temp;
}
void Qsort(ElementType A[],int Low,int High){
//以第一个元素为基准
ElementType Pivot=A[low];
//保存初始的最大最小元素位置
int Left=Low,Right=High;
if(Low>=High) return;
//基准先放到最后
Swap(&A[Low],&A[High]);
//将比基准小的移到基准左边,比基准大的移到基准右边
while(1){
while((Low<High)&&(Pivot>=A[Low])) Low++;
while((Low<High)&&(Pivot<=A[High])) High--;
if(Low<High)
Swap(&A[Low],&A[High]);
else break;
}
//基准回到中间
Swap(&A[Low],&A[Right]);
//分别对左右两个边的序列继续快排
Qsort(A,Left,Low-1);
Qsort(A,low+1,Right);
}
//执行快排
void QuikSort(ElementType A[],int N){
Qsort(A,0,N-1);
}
#归并排序
归并排序是指将两个已排序的子序列合成一个有序序列的过程。原理是:可以将大小为N的序列看成N个长度为1的子序列,然后两两归并,形成一个N/2长度的长度为2的子序列有序序列,然后继续将相邻的子序列两两归并,如此循环直到最后剩下一个长度为N的序列。
//将两个序列归并
void merge(ElementType A[],ElementType TmpA[],int Left,int Mid,int Right){
int Tp,LeftEnd,i;
Tp=Left;
LeftEnd=Mid-1;
while((Left>LeftEnd)&&(Mid<Right)){
//如果左序列元素比右序列大,则将左边元素移入临时序列
if(A[Left]<=A[Mid])
TmpA[Tp++]=A[Left++];
else
//如果右序列元素比左序列大,则将右边元素移入临时序列
TmpA[Tp++]=A[Mid++];
}
while(Left<=LeftEnd)//满足说明右边序列元素已经没了
//将剩余的左边元素复制到临时序列
TmpA[Tp++]=A[Left++];
while(Mid<=Right)//满足说明左边序列元素已经都没了
//将剩余的右边元素复制到临时序列
TmpA[Tp++]=A[Mid++];
//将临时序列放回到A中
for(i=Right-Left;i>=0;i--,Right--){
A[Right]=TmpA[Right];
}
}
//递归排序
void Msort(ElementType A[],ElementType TmpA[],int Left,int Right){
if(Left<Right){
Msort(A,TmpA,Left,(Left+Right)/2);
Msort(A,TmpA,(Left+Right)/2+1,Right);
merge(A,TmpA,Left,(Left+Right)/2,Right);
}
}
//排序方法(执行方法)
void MergeSort(){
ElementType *TmpA;
TmpA=malloc(N*sizeof(ElementType));
Msort(A,TmpA,0,N-1);
free(TmpA);
}
#基数排序
基数排序可以看成桶排序。桶排序是将关键字的每个可能的取值建立一个桶,扫描所有序列元素,按照关键字放入对应的桶中,然后再按照桶的顺序收集一遍自然就有序。而基数排序属于桶排序的一种推广,所考虑的待排序记录的关键字不止一个。
对于一般有K个关键字的基数排序,通常有两种方法:主位优先法(MSD)和次位优先法(LSD)。
下面是次位优先法的C语言代码解释实现(有待研究):
typedef struct Node *PtrToNode;
typedef PtrToNode List;
struct Node{
int Key[MaxDigit];
PtrToNode Next;
};
List RadixSort(List A){
List Bucket[Radix];//建立Radix个桶
List Bear[Radix];//需要记录每个桶的链表的尾元素的位置
int i,j,Digit;
for(i=MaxDigit-1;i>=0;i--){//从最次关键字开始
for(j=0;j<Radix;j++)
Bucket[j]=Rear[j]=NULL;
while(A){//将关键字逐一分配到桶
Digit=A->Key[i];
if(!Bucket[Digit]){
Bucket[Digit]=A;
}else{
Rear[Digit]->Next=A;
}
Rear[Digit]=A;
A=A->Next;
}
for(j=Radix-1;j>=0;j--){
if(Bucket[j]){
Rear[j]->Next=A;
A=Bucket[j];
}
}
}
return A;
}
#各算法效率比较
排序方法 | 平均时间复杂度 | 最坏情况的时间复杂度 | 额外的空间复杂度 | 稳定性 |
---|---|---|---|---|
简单选择排序 | O(N2) | O(N2) | O(1) | 不稳定 |
简单插入排序 | O(N2) | O(N2) | O(1) | 稳定 |
冒泡排序 | O(N2) | O(N2) | O(1) | 稳定 |
希尔排序 | O(Nd) | O(N2) | O(1) | 不稳定 |
堆排序 | O(N log2N) | O(N log2N) | O(1) | 不稳定 |
快速排序 | O(N log2N) | O(N2) | O(log2N) | 不稳定 |
归并排序 | O(N log2N) | O(N log2N) | O(N) | 稳定 |
基数排序 | O(D(N+R)) | O(D(N+R)) | O(N+R) | 稳定 |
注:
1、稳定是指是否在排序后改变原有等值元素的顺序。
2、基数排序中的D为分配收集的趟数,也就是关键字按基数分解后的位数
#以上
参考文献:高等教育出版社出版的陈越主编的《数据结构》