排序:利用数组将一组数据,按指定的顺序进行排列
分类:内部排序:将要处理的所有数据都加载到内部存储器中进行排序。(八种排序)
-
插入排序:
- 直接插入O(n2):将无序元素通过比较,插入有序空间
- 希尔排序: 简单的插入排序每次只比较两个元素只能交换两个错位的元素,所以效率很低,希尔排序在此基础上通每次比较(并交换)相隔一定距离的元素,从而提高排序效率。
-
选择排序:
- 简单选择O(n2):找到数组中最小的元素,并和第一个元素进行交换,在剩下的元素中找到最小的元素,与数组中的第2个元素进行交换,如此往复,直到有序
- 堆排序: 堆排序就是利用堆(优先队列)对序列进行排序,因为优先级最高的元素永远在堆顶,所以我们可以一直取堆顶元素然后重新生成堆直到有序。这个过程其实就是根据上面的冒泡排序,把找最优元素的任务交给了堆 来降低时间复杂度。
-
交换排序:
- 冒泡排序O(n2): 进行N-1次循环,在第k次循环中从0开始到N-k-1每次比较相邻的两个元素,看是否需要交换。
- 快速排序: 和插入排序时一样,冒泡排序每次只比较相邻的两个元素,所以效率也会很低,如果我们每次取未排序的序列中的一个元素(称为主元)把未排序的序列分成两个子序列,其中一个全部小于主元,另一个全部大于主元,然后递归地进行这样的排序。
-
归并排序: 顾名思义,归并排序就是根据归并操作的原理,把序列中的N个元素看成N个有序的子序列,每次合并两个相邻有序子序列,直到剩下一个长度为N的 序列,即为原序列排好序后的有序序列
- 利用归并思想进行排序,采用了分治策略。
-
基数排序: 是桶排序的延伸,排序时不止考虑单个关键字。其实就是单纯用一种关键字来排序效率低,我们可以对于在一种分类后子序列还能分为许多种子序列(也就是多层关键字)的情况用基数排序,也就是将单个整形关键字按某种基数分解为多关键字,再进行排序。
-
堆排序:就是利用堆(优先队列)对序列进行排序,因为优先级最高的元素永远在堆顶,所以我们可以一直取堆顶元素然后重新生成堆直到有序
-
外部排序:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。
算法的时间复杂度O(f(n)):
-
事后统计法:先运行再统计:1.麻烦;2.不准确,必须在同一台计算机的相同状态下运行才能比较(不推荐)
-
事前估算法:分算法的时间复杂度来判断,复杂度是忽略常数的(指数级)
时间频度T(n):算法花费的时间与算法中语句的执行次数成正比
比如求连续数字之和:total = (1+end)*end/2; T(n) = 1;
而常规的算法 T(n) = n+1; 1.忽略常数 2.忽略低次项 3.忽略系数
计算时间复杂度的方法:
1.常数项用1代替
2.只保留最高阶项
3.去除最高阶项的系数
*对数阶logN < 线性阶n < 线性对数阶nlogN
无论代码执行多少行,若没有循环等复杂结构,时间复杂度为O(1);
while(i < n)i = i*2; 时间复杂度为logN
消耗的时间随n的变换而变化 O(n)
将时间复杂度为O(logN)的代码循环n边 nlogN
双层for循环 O(n^2)
i层for循环 O(n^i) -
-
空间复杂度:占用的储存空间大小(空间换时间):用户体验来看,更看中程序的执行速度
-
稳定性:
- a原本在b的前面,而a=b,排序之后a仍在的前面,就是稳定的
- 若a可能出现在b的后面,就是不稳定的
没有最好的算法,只有最合适的算法
- 当数据规模较小时候,可以使用简单的直接插入排序或者直接选择排序。
- 当文件的初态已经基本有序,可以用直接插入排序和冒泡排序。
- 当数据规模较大时,应用速度最快的排序算法,可以考虑使用快速排序。当记录随机分布的时候,快速排序平均时间最短,但是会出现最坏的情况,这个时候的时间复杂度是O(n^2),且递归深度为n,所需的占空间为O(n)。
- 堆排序不会出现快排那样最坏情况,且堆排序所需的辅助空间比快排要少,但是这两种算法都不是稳定的,要求排序时是稳定的,可以考虑用归并排序。
- 归并排序可以用于内部排序,也可以使用于外部排序。在外部排序时,通常采用多路归并,并且通过解决长顺串的合并,加上长的初始串,提高主机与外设并行能力等,以减少访问外存额外次数,提高外排的效率。
- 特殊的桶排序、基数排序都是稳定且高效的排序算法,但有一定的局限性:
- 关键字可分解。
- 记录的关键字位数较少,如果密集更好
- 如果是数字时,最好是无符号的,否则将增加相应的映射复杂度,可先将其正负分开排序。