排序的稳定性和复杂度
不稳定:
选择排序(selection sort)— O(n2)
快速排序(quicksort)— O(nlog2n)平均时间, O(n2)最坏情况;对于大的、乱序串列一般认为是最快的已知排序
堆排序 (heapsort)— O(nlog2n)
希尔排序 (shell sort)— O(n(1+s)) s为0~1的数
基数排序(radix sort)— O(n·k)需要O(n)额外存储空间 (K为特征个数)
稳定:
插入排序(insertion sort)— O(n2)
冒泡排序(bubble sort)— O(n2)
归并排序 (merge sort)— O(nlog2n);需要O(n)额外存储空间
二叉树排序(Binary tree sort)— O(nlogn);需要O(n)额外存储空间
计数排序 (counting sort) — O(n+k);需要O(n+k)额外存储空间,k为序列中Max-Min+1
桶排序 (bucket sort)— O(n);需要O(k)额外存储空间
插入排序
插入排序的基本思想是:将第i个记录的关键字Ki与前面记录r[1]~r[i-1]的关键字从后向前进行顺次比较,将所有关键字大于Ki的记录依次向后移动一个位置,直到遇到一个关键字小于或等于Ki的记录,该记录的位置即为r[i]的插入位置。
如:
将33,12,25,46,33,68,19,80排序
void InsertSort(int r[], int n)//n为需要比较数个数
{
int i,j;
for(int i=2;i<=n;i++)
{
if(r[i]<r[i-1])//如果r[i]>r[i-1],则为正确序列,不用排序
{
r[0]=r[i];//监视哨备份,即将r[i]作为关键字
for(j=i-1;r[0]<r[j];j--)
{
r[j+1]=r[j]; //记录后移
}
r[j+1]=r[0];//插入到正确位置
}
}
}
希尔排序
希尔排序的基本思想是:将需要排序的序列划分成为若干个较小的子序列,对子序列进行插入排序,通过则插入排序能够使得原来序列成为基本有序。这样通过对较小的序列进行插入排序,然后对基本有序的数列进行插入排序,能够提高插入排序算法的效率。
希尔排序的划分子序列不是像归并排序那种的二分,而是采用的叫做增量的技术,例如有八个元素的数组进行希尔排序,首先选择增量为8/2=4。
r[1]和r[5]比较,r[2]和r[6]比较……
如:
将46,25,68,33,33,19,12,80排序
void ShellSort(int r[], int n)//n为需要比较数个数
{
int i,j;
int dk;//dk为增量
for(dk=n/2;d>=1;dk=dk/2)
{
for(i=dk+1;i<=n;i++)
{
if(r[i]<r[i-dk])//如果r[i]>r[i-dk],则为正确序列,不用排序
{
r[0]=r[i];//监视哨备份,即将r[i]作为关键字
for(j=i-dk;j>0&&r[0]<r[j];j-=dk)
{
r[j+dk]=r[j]; //记录交换
}
r[j+dk]=r[0];//插入到正确位置
}
}
}
}
冒泡排序
冒泡排序的基本思想是:顺次比较相邻两个记录的关键字大小,如果逆序就交换位置。
void BubbleSort(int r[], int n)//n为需要比较数个数
{
int i,j,temp;
int flag=1;//flag为标志位
for(i=1;i<=n-1&&flag;i++)
{
flag=0;//如果顺序正确则不进行比较,直接输出。
for(j=1;j<=n-i;j++)
{
if(r[j]>r[j+1])
{
temp=r[j];
r[j]=r[j+1];
r[j+1]=temp;
flag=1;
}
}
}
}
快速排序
快速排序的基本思想是:从待序列中任意选择一个记录,以该记录为关键字,凡小于该关键字的记录均移动到该记录之前,大于该关键字的记录移动到该关键字之后。致使一趟排序之后,记录的无序序列被分割成左右两个子序列,然后再分别对两个子序列进行递归快排,直到每个子序列只含有一个记录为止。
如:
将46,68,12,25,33,80,19,33排序
void QKpass(int r[],int low,int high)//low,high分别是标志位
{
r[0]=r[low];
while(low<high)//当标志位重叠时结束
{
while(low<high&&r[high]>r[0])//如果high>关键字,前移
{
--high;
}
r[low]=r[high];//如果high<关键字,将high赋给low
while(low<high&&r[low]<r[0])//如果low<关键字,后移
{
++low;
}
r[high]=r[low]; //如果low>关键字,将low赋给high
}
r[low]=r[0];//当标志位重叠时,将r[0]关键字赋值给r[low]
return low;//返回标志位
}
void QKSort(int r[],int low,int high)
{
int pos;
if(low<high)
{
pos=QKpass(r,low,high);
QKSort(r,low,pos-1);//对比关键字小的部分序列的快排
QKSort(r,pos+1,high);//对比关键字大的部分序列的快排
}
}
简单选择排序
简单选择排序的基本思想是:从第一个记录开始遍历,每一趟找出一个最小值。即,第一趟,r[1]与r[2]~r[n]每一个进行比较,如果r[i]<r[1]
,则r[1]和r[i]值进行交换。第二趟,r[2]与r[3]~r[n]每一个进行比较,如果r[i]<r[2]
,则r[2]和r[i]值进行交换…….
如:
将33,68,46,33,25,80,19,12排序
void SelectSort(int r[],int n)
{
int k,temp;
for(int i=0;i<=n-1;i++)
{
k=i;
for(j=i+1;j<=n-1;j++)
{
if(r[j]<r[k])
{
k=j;
}
}
if(k!=i)
{
temp=r[i];
r[i]=r[k];
r[k]=temp;
}
}
}
堆排序
堆排序的基本思想是:先建一个“大顶堆”,即先选一个关键字最大的记录,然后与序列中最后一个记录交换,之后继续对序列中前n-1记录进行“筛选”,重新将它调整为一个“大顶堆”,再将堆顶记录和第n-1个记录交换,如此反复,直至排序结束。
如:
对46,12,33,72,68,19,80,33进行堆排序
堆的筛选,过程如下:
void HeapAdjust(int r[],int s,int n)
{
int temp=r[s];
for(int j=2*s;j<=n;j*=2)
{
if(j<n&&r[j]>r[j+1])//沿值较小的节点向下筛选
j++;
if(temp<r[j])
break;
r[s]=r[j];
s=j;
}
r[s]=temp;//插入
}
建初始堆过程如下:
void CreatHeap(int r[],int n)
{
for(i=n/2;i>=1;i--)
HeapAdjust(r,i,n);
}
堆排序过程如下:
void HeapSort(int r[],n)
{
CreateHeap(r,n);
for(i=n;i>=2;i--)
{
r[0]=r[1];
r[1]=r[i];
r[i]=r[0];
HeapAdjust(r,1,i-1);
}
}
基数排序
基数排序的基本思想是:对于数字型或字符型的单关键字,可以看做是由多个位数或多个字符构成的多关键字。
如:
对921,435,628,285,862,225,448,193,430排序。
首先按照“个位数”取值,分别为0,1…….,9“分配”成10组,之后按照0到9收集在一起。
然后按照“十位数”分配收集。
然后“百位数”。
然后……
//提取关键字第m位的数字值
int digit(KeyType k,int m,int r)
{
int i,d;
if(m==0)
return k%r;
d=r;
for(i=1;i<m;i++)
d*=r;
return ((int)(key/d)%r);
}
//基数排列
void RadixSort(Type r[],int n,int m,int r)//L中的关键字为m位r进制数,L的长度为n
{
LinkQueue *Queue;
int i,j,k;
q=(LinkQueue *)malloc(r*sizeof(LinkQueue));
for(i=0;i<r;i++)
InitQueue(&Queue[i]);
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
k=digit(L[j].key,i,r);
EnterQueue(&Queue[k],L[j]);
}
k=0;
for(j=0;j<r;j++)
for(;!IsEmptyQueue(Queue[j]);k++)
DeleteQueue(&Queue[j],&(L[k]));
}
}