排序算法可以说是比较基础的算法了,这次先介绍排序刷算法的一部分。
笔者的编程环境:
- 语言:c++
- 编译器: Code Blocks
- 系统环境:window 10 x64
排序算法的几种名词
稳定性
等值元素的相对位置是否会发生变化。稳定则是指不会发生变化;不稳定则是指可能会发生变化。
时间复杂度
定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
空间复杂度
空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小,它包括为参数表中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。若一个算法为递归算法,其空间复杂度为递归所使用的堆栈空间的大小,它等于一次调用所分配的临时存储空间的大小乘以被调用的次数(即为递归调用的次数加1,这个1表示开始进行的一次非递归调用)。算法的空间复杂度一般也以数量级的形式给出。如当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1);当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为O(log2n);当一个算法的空间复杂度与n成线性比例关系时,可表示为O(n).若形参为数组,则只需要为它分配一个存储由实参传送来的一个地址指针的空间,即一个机器字长空间;若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。
总结
对于一个算法,其时间复杂度和空间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。另外,算法的所有性能之间都存在着或多或少的相互影响。
因此,当设计一个算法(特别是大型算法)时,要综合考虑算法的各项性能,算法的使用频率,算法处理的数据量的大小,算法描述语言的特性,算法运行的机器系统环境等各方面因素,才能够设计出比较好的算法。算法的时间复杂度和空间复杂度合称为算法的复杂度。
冒泡排序
基本思想
- 从后往前两两比较相邻元素的值,若是逆序则交换,直至最后。
- 从倒数第二个数开始,从新执行第一步。
- 若某一次执行第一步没有任何交换或执行了(n-1)次第一步则表示排序结束。
空间复杂度:O(1)
时间复杂度:O(n^2)
稳定性:稳定
代码如下:
template<typename T>
int swap(T &a,T &b)
{
T c;
c=a;
a=b;
b=c;
return 0;
}
template<typename T>
int sort(T arr[],int count)
{
for(int i=0,j=0,flag=1; i<count-1; i++)
{
for(j=count-1; j>i; j--)
{
if(arr[j-1]>arr[j])
{
swap(arr[j-1],arr[j]);
flag--;
}
}
if(flag>0)break;
}
return 0;
}
选择排序
基本思想
- 从前往后扫描得最小元素,与第一个元素交换。
- 从第二个数开始,从新执行第一步。
- 执行了(n-1)次第一步则表示排序结束。
空间复杂度:O(1)
时间复杂度:O(n^2)
稳定性:不稳定
代码如下:
template<typename T>
int swap(T &a,T &b)
{
T c;
c=a;
a=b;
b=c;
return 0;
}
template<typename T>
int sort(T arr[],int count)
{
for(int i=0;i<count-1;i++)
{
int min=i;
for(int j=i+1;j<count;j++)
{
if(arr[j]<arr[min])min=j;
}
if(min-i) swap(arr[i],arr[min]);
}
return 0;
}
插入排序
基本思想
- 将第一个元素当作最初的有序序列。
- 从第二个数开始将元素插入到前面的有序序列中。
2.1. 从有序序列最后开始查找到第一个不大于当前元素的位置。(顺序查找或折半查找,以折半查找为例)
2.2. 将查找到的位置后的元素后移一个位置,将当前元素插入到空出来的位置。
空间复杂度:O(1)
时间复杂度:O(n^2)
稳定性:稳定
代码如下:
template <typename T>
int sort(T arr[],int count)
{
for(int i=1,j=0;i<count;i++)
{
T temp=arr[i];
int low=0,high=i-1,mid=0;
while(high>=low)//查找位置
{
mid=(low+high)/2;
if(arr[mid]>temp)high=mid-1;
else low=mid+1;
}
for(j=i-1;j>=high+1;j--)
{
arr[j+1]=arr[j];//元素后移
}
arr[high+1]=temp;//元素插入
}
return 0;
}
希尔排序
希尔排序又叫缩小增量排序,是对插入排序的一种优化。
基本思想
- 设置一个小于待排序元素数量的增量dk
- 将待排序序列按增量dk分组:{N* dk},{N* dk+1}…。(N=1,2,3,4…)
- 将每个分组按插入排序进行排序
- 不断缩小dk直至dk=1
空间复杂度:O(1)
时间复杂度:涉及到的数学未解难题,当dk处于一定范围内时,时间复杂度可以达到O(n^1.3 ),最差时是O(n^2)
稳定性:不稳定
代码如下:
template <typename T>
int sort(T arr[],int count)
{
for(int dk=count/2;dk>0;dk/=2)
{
for(int i=dk,j=0;i<count;i++)
{
if(arr[i]<arr[i-dk])
{
T temp=arr[i];
for(j=i-dk;j>0&&temp<arr[j];j-=dk)
{
arr[j+dk]=arr[j];
}
arr[j+dk]=temp;
}
}
}
return 0;
}