1. 经典排序算法
1.1 插入排序
从头到尾遍历数组,当前元素应该插入到小于等于它的最右下标之后。实际使用相邻元素交换的方法
public void insertSort(int[] nums){
for(int i=1;i<n;i++){
for(int j=i;j>0;j--){
if(nums[j]<nums[j-1]){
swap(nums,j,j-1);
}
}
}
}
public void swap(int[] nums,int i,int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
时间复杂度
| 最好 | 平均 | 最坏 |
|---|---|---|
| O(n) | O(n^2) | O(n^2) |
1.2 快速排序
分治思想,不断分割序列直到整体有序
- 选定一个pivot
- 遍历数组,分成比pivot小和比pivot大的左右序列
- 递归,分别继续对左右序列进行类似处理
时间复杂度
| 最好 | 平均 | 最坏 |
|---|---|---|
| O(n*logn) | O(n*logn) | O(n^2) |
1.3 堆排序
利用堆的性质进行排序 每次将大顶堆的堆顶元素交换到末尾,再调整堆结构 时间复杂度
| 最好 | 平均 | 最坏 |
|---|---|---|
| O(n*logn) | O(n*logn) | O(n*logn) |
1.4 综合考虑
- 在短序列或元素有序情况下,插入排序性能最好
- 大部分情况下,快速排序有较好的综合性能
- 在任何情况下,堆排序表现较稳定
2. 从零设计qdqsort
结合三种排序算法的优点
- 对于短序列,使用插入排序
- 其他情况,使用快速排序保证整体性能
- 快速排序表现不佳时,使用堆排序保证稳定的时间复杂度O(n*logn)
Q:
-
短序列具体长度?
12~32,具体依据不同语言和场景
-
如何得知快速排序性能不佳?
当pivot离数组两段很接近(距离小于length/8),认为其表现不佳 当表现不佳的情况次数达到一定数值,切换为堆排序
如何设计高性能的排序算法
根据不同情况选择不同策略,取长补短