排序部分知识点Part2。
荷兰国旗问题
1 | public class Code_08_NetherlandsFlag { |
随机快速排序
可以用荷兰国旗问题来改进快速排序,经典快排一次partition只搞定一个数
1 | public class Code_04_QuickSort { |
- 时间复杂度:O(N*logN)
- 额外空间复杂度:O(logN) 因为每次partition需要记住索引
堆排序
堆——完全二叉树 {大根堆,小根堆} 以0开始数组存储
左孩子:2×i+1
右孩子:2×i+2
父节点:(i-1)/2
堆结构非常重要:
- 堆结构的 heapInsert 和 heapify
- 堆结构的增大和减小
- 如果只是建立堆的过程,时间复杂度为O(1)
- 优先级队列结构,就是堆结构
heapInsert 操作 :O(log(N-1))
建立大根堆复杂度:log1+log2+…+log(N-1) ->O(N)heapify 操作:
堆排序经历N次heapify:log1+log2+…+logN -> O(N*log(N))
以大根堆为例:
1 | public class Code_03_HeapSort { |
- 堆排序的时间复杂度:O(N*logN)
- 空间复杂度:O(1)
排序算法的稳定性汇总
- O(n^2)
- 冒泡排序 可以实现成稳定的 遇到相等的不交换
- 插入排序 可以实现成稳定的 遇到相等的不往前交换
- 选择排序 不可以实现成稳定的 555503
- O(nlogn)
- 归并排序 可以实现成稳定的 merge过程中先copy左边的
- 快速排序 不可以实现成稳定的 partition过程不能实现稳定性
- 堆排序 不可以实现成稳定的 44455 仅仅建立大根堆的过程就不能维持稳定性
有关排序问题的补充
- 归并排序的额外空间复杂度可以变成O(1),但非常难,可以搜“归并排序 内部缓存法”
- 快排可以做到稳定性,但非常难,可以搜“01 stable sort”
- 有一道搞人题目,奇数放在数组左边,偶数放在数组右边,还要求维持原始的相对次序不变,O(N)时间复杂度,O(1)空间复杂度 —— 等同于要实现快排的稳定性
比较器的使用
1 | package basic_class_01; |
桶排序、计数排序、基数排序
- 非基于比较的排序,与被排序的样本的实际数据状况很有关系,所以实际中并不常用
- 时间复杂度O(N),额外空间复杂度O(N)
- 稳定的排序
桶排序是一个大的逻辑概念,计数排序和基数排序是桶排序的一种实现。
1 | package basic_class_01; |
工程中的综合排序算法
先判断数据类型,基础类型->快排, 定义类型->比较器,归并排序;
数组长度很短(小于60)-> 不管什么类型都用插入排序,因为插入排序常数项很低;
归并或快排过程中,L part和R part 长度一旦小于60,插入排序;
基础数据类型不需要考虑稳定性,相同值无差异,所以选择快排;
而定义类型需要考虑稳定性。