最近我整理了经常用到的排序算法。排序就是按关键字的递减或递增顺序对一组记录重新进行整队的操作。对于排序来说,主要考虑的因素就是时间复杂度、空间复杂度和算法的复杂度。
下面我先整理简单选择排序,直接插入排序,折半插入排序,希尔排序和冒泡排序这几种。以下的所有的代码都是用C语言实现的,测试用例是int型数组。复杂排序方法将在我的下一篇博客中整理。
一、简单选择排序
1、排序思想首先,在带排序序列中选择出最小记录,然后将这个最小数据元素与第一个记录交换,第一个到位,这只是第一趟排序,第二趟就是从第二个记录到最后一个记录中选择最小的记录,之后将这个最小记录与第二个记录交换,第二个记录到位,以此类推。进行n-1趟,序列就有序了。
2、代码实现
//简单选择排序
void SelectSort(int *a,int length)
{
int min=0,t=0;//用来记录最小的数的下标
//从0到length-1,每趟都选择出最小的数保存在相应的位置
for(inti=0;i<length-2;i++)
{
min=i;//每趟进来,都默认第一个数为最小值
for(intj=i+1;j<length;j++)
{
if(a[j]<a[min])
{
min=j;
}
}
//将第i趟的最小值放入i的位置
t=a[i];
a[i]=a[min];
a[min]=t;
}
}
3、效率分析
时间复杂度:在第i趟选择排序中,需进行n-i次关键字的比较,交换记录时需要3次移动记录的操作。最好情况就是正序排序,不需要移动记录,最坏情况即逆序排序,需要移动次数最多为3(n-1)。一般情况下需要进行比较次数是:n(n-1)/2。所以时间复杂度为O(n2).
空间复杂度:需要一个辅助工作变量,因此,它的空间复杂度为O(1)。
稳定性:就选择排序方法本身来讲,它是一种稳定的排序方法。但是也可能受到排序方法采用的“交换记录”的策略所影响。
二、直接插入排序
1、算法思想
在一个已排好序的记录子集的基础上,每一步将下一个带排序的记录有序的插入到已排好序的记录子集中,直到将所有记录全部插入为止。就像打扑克牌的时候,一张张抓牌到自己手中,插入到已排好序的牌的过程。
2、代码实现
//直接插入排序
void InsertSort(int * a,int length)
{
int temp;
//取待排序序列中的每一个数,将这个数插入到已排序序列中
for(inti=1;i<=length-1;i++)
{
temp=a[i];
if(a[i]<a[i-1])
{
for(intj=i-1;temp<a[j];j--)
a[j+1]=a[j];
a[j+1]=temp;
}
}
}
void InsertSort2(int * a,int length)
{
int temp;
//取待排序序列中的每一个数,将这个数插入到已排序序列中
for(inti=0;i<=length-1;i++)
{
temp=a[i];
for(intj=i-1;j>=0&&temp<a[j];j--)
a[j+1]=a[j];
a[j+1]=temp;
}
}
3、效率分析
时间复杂度:O(n2)。空间复杂度:O(1)。
稳定性:是稳定的排序方法。
三、折半插入排序
1、排序思想
折半插入排序是对直接插入排序的改进,直接插入排序插入位置的确定是通过对有序表中记录按关键字逐个比较得到的,而折半插入排序是采用折半查找在有序表中确定插入位置。
2、代码实现
//折半查找插入排序
void BinSort(int *a,int length)
{
int temp;
for(inti=1;i<=length-1;i++)
{
temp=a[i];
int low=0;
inthigh=i-1;//确定有序序列的区间
while(low<=high)
{
intmid=(low+high)/2;
if(temp<a[mid])
high=mid-1;
else
low=mid+1;
}
for(intj=i-1;j>=low;j--)
{
a[j+1]=a[j];
}
a[low]=temp;
}
}
3、效率分析
时间复杂度:虽然折半查找在查找插入位置的方面性能有所提高,但插入到有序表的过程是不变的,所以时间复杂度还是 O(n2)。
四、希尔排序
1、排序思想
希尔排序是对直接插入排序的改进,直接插入排序对n值较小的序列效率较高。希尔排序的基本思想就是先将整个待排记录序列分割成若干个子序列,对每个子序列分别进行直接插入排序,待整个序列中记录基本有序时,再对全体记录进行一次直接插入排序。希尔排序中子序列的构成不是简单的逐段分割,而是将相隔某个增量的记录组成一个子序列。
2、代码实现
#include<stdio.h>
//希尔排序
void ShellSort(int *a,int length,int * delta,int n)
{
int temp;
for(inti=0;i<n;i++)
{
intta=delta[i];
for(intj=0+ta;j<=length-1;j+=ta)
{
temp=a[j];
for(intk=j-ta;k>=0&&temp<a[k];k-=ta)
a[k+ta]=a[k];
a[k+ta]=temp;
}
}
}
int main(void)
{
inta[]={39,80,76,41,13,29,50,78,30,11,100,7,41,86};
int b[]={5,3,1};
for(inti=0;i<14;i++)
{
printf("%d\t",a[i]);
}
printf("\n");
ShellSort(a,14,b,3);
for(i=0;i<14;i++)
{
printf("%d\t",a[i]);
}
printf("\n");
return 0;
}
3、效率分析
希尔排序的时效分析是一个复杂的问题,关键字的比较次数与记录移动的次数依赖于增量因子序列的选取,特定情况下可以准确估算出关键字的比较次数和记录的移动次数。希尔排序是一种不稳定的排序算法。
五、冒泡排序
1、代码实现
//冒泡排序
void BubbleSort(int *a,int length)
{
int temp;
for(inti=0;i<length-1;i++)
{
for(intj=0;j<length-1-i;j++)
{
if(a[j]>a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
}
2、效率分析
时间复杂度:O(n2)。
空间复杂度:O(1)。稳定性:稳定。