【内部排序八大排序算法】C语言详解 -- 稳定不稳定区分,最好最坏时间复杂度
概述
排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
算法优劣术语
稳定性与不稳定性
稳定:若果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:若果a原本在b前面,而a=b,排序之后a可能会出现在b的后面。
稳定性意义的探讨
-
如果只是简单的进行数字的排序,那么稳定性将毫无意义。
-
如果排序的内容仅仅是一个复杂对象的某一个数字属性,那么稳定性依旧将毫无意义(所谓的交换操作的开销已经算在算法的开销内了,如果嫌弃这种开销,不如换算法好了?)
-
如果要排序的内容是一个复杂对象的多个数字属性,但是其原本的初始顺序毫无意义,那么稳定性依旧将毫无意义。
-
除非要排序的内容是一个复杂对象的多个数字属性,且其原本的初始顺序存在意义,那么我们需要在二次排序的基础上保持原有排序的意义,才需要使用到稳定性的算法,例如要排序的内容是一组原本按照价格高低排序的对象,如今需要按照销量高低排序,使用稳定性算法,可以使得想同销量的对象依旧保持着价格高低的排序展现,只有销量不同的才会重新排序。(当然,如果需求不需要保持初始的排序意义,那么使用稳定性算法依旧将毫无意义)
时空复杂度
时间复杂度:一个算法执行所耗费的时间。
空间复杂度:运行一个程序所需要内存的大小。
一般情况下,当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
时间空间复杂度总结对比
插入排序—直接插入排序(Straight Insertion Sort)
插入排序
是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
-
算法描述:每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。为了给要插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位。
-
要点:设立哨兵,作为临时存储和判断数组边界之用。
直接插入排序示例:
-
如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
-
代码
void insert_sort(int array[],unsigned int n)
{
int i,j;
int temp;
for(i = 1;i < n;i++)
{
temp = array[i];
for(j = i;j > 0&& array[j - 1] > temp;j--)
{
array[j]= array[j - 1];
}
array[j] = temp;
}
}
- 算法复杂度
其他的插入排序有二分插入排序,2-路插入排序。
插入排序—希尔排序(Shell`s Sort)
希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进。希尔排序又叫缩小增量排序,但希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
-
插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
-
但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位。
基本思想:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录 “基本有序”时 ,再对全体记录进行依次直接插入排序。 -
算法描述:
-
选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
-
按增量序列个数k,对序列进行k 趟排序;
-
每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
-
-
示例:
-
要点:
我们简单处理增量序列:增量序列d = {n/2 ,n/4, n/8 .....1}
n为要排序数的个数
即:先将要排序的一组记录按某个增量d(n/2,n为要排序数的个数)分成若干组子序列,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,
然后再用一个**较小的增量(d/2)**对它进行分组,在每组中再进行直接插入排序。继续不断缩小增量直至为1,最后使用直接插入排序完成排序。 -
代码
#include<stdio.h>