Collections.sort()方法底层使用Arrays.sort()实现的!
而Arrays中对于集合类的sort()方法主要采用TimSort算法(优化的归并算法)(对应数组的算法主要采用DualPivotQuicksort.sort()(优化的快速排序算法)), 大致思路是这样的:
- 元素个数 < 32, 采用二分查找插入排序(Binary Sort)
- 元素个数 >= 32, 采用归并排序,归并的核心是分区(Run)
- 找连续升或降的序列作为分区,分区最终被调整为升序后压入栈
- 如果分区长度太小,通过二分插入排序扩充分区长度到分区最小阙值
- 每次压入栈,都要检查栈内已存在的分区是否满足合并条件,满足则进行合并
- 最终栈内的分区被全部合并,得到一个排序好的数组
Timsort的合并算法非常巧妙:
- 找出左分区最后一个元素(最大)及在右分区的位置
- 找出右分区第一个元素(最小)及在左分区的位置
- 仅对这两个位置之间的元素进行合并,之外的元素本身就是有序的
而所有的排序(二叉树也是)的比较都是基于compareTo()实现的!!!
所以要排序一个自定义的对象,那么:
4. 该类一定要实现Comparable< T >接口实现compareTo()方法;
5. 或者构造一个比较器,实现Comparator< T >的compare()方法!!!
如何优化快速排序?
如果数据原来就是有序的或者接近有序的,每次分区点都选择最后一个数据,那快速排序算法就会变得非常糟糕,时间复杂度就会退化为 O(n2)。实际上,这种 O(n2) 时间复杂度出现的主要原因还是因为我们分区点选得不够合理。
1. 三数取中法(主要)
我们从区间的首、尾、中间,分别取出一个数,然后对比大小,取这 3 个数的中间值作为分区点。这样每间隔某个固定的长度,取数据出来比较,将中间值作为分区点的分区算法,肯定要比单纯取某一个数据更好。但是,如果要排序的数组比较大,那“三数取中”可能就不够了,可能要“五数取中”或者“十数取中”。
2. 随机法
随机法就是每次从要排序的区间中,随机选择一个元素作为分区点。这种方法并不能保证每次分区点都选的比较好,但是从概率的角度来看,也不大可能会出现每次分区点都选得很差的情况,所以平均情况下,这样选的分区点是比较好的。时间复杂度退化为最糟糕的 O(n2) 的情况,出现的可能性不大。
递归优化
1.限制递归深度
一旦递归过深,超过了我们事先设定的阈值,就停止递归。