7大基于比较的排序:插入排序|希尔排序、选择排序|堆排序、冒泡排序、快速排序、归并排序
**注意
1.排序:默认情况都是按非降序排序(In-place 原地排序)
2.稳定的排序:具有稳定性(即能保证排序过程中相等的数据的相对顺序不变)。
3.插入排序:(减治算法排序)
每次从无序区间选择第一个数,插入到有序区间的合适位置,直到全部有序。
有序区间 | 无序区间 |
//法一:
public static void insertSort(int[]array){
for(int i =0; i<array.lenghth-1;i++){
//有序区间:[0,i] 无序区间[i+1,array.lenghth]
//待插入的数据是:array[i+1]
//插入过程在有序区间进行查找
int key = array[i+1];
int j;
for(j=i;j >= 0 && key < array[j];j--){
array[j+1] = array[j];
}
array[j+1] = key;
}
}
//法二:
public static void insertSort1(int[] array){
for(int i=0;i<array.length-1;i++){
int key = array[i+1];
int j;
for(j= i;j>=0;j--){
if(key >= array[j]){
break;
}
//j+1 放 key 的位置
for(int k = i;k >j;k--){
array[j+1] = key;
}
}
}
}
*3.1思路:如果有n个数,则外层循环需循环n-1次
(1)**查找:从后往前找
(2)**插入:每次都把无序区间的第一个数,在有序区间内遍历(从后往前遍历)
1.一共需要进行多少次插入? size-1(size次也可以)
2.找到合适的位置,搬移原有数据,为该数据腾出位置。
3.2时间复杂度:
最好 | 平均 | 最坏 |
O(n) 完全有序(从后往前比较) | O(n^2) | O(n^2) |
3.3空间复杂度:O(1)
3.4稳定性:稳定
3.5 总结:插入排序,越接近有序,执行时间效率越高
3.6 计时:
long begin = System.nanoTime(); //纳秒(打印前计时)
long end = System.nanoTime(); //纳秒 (打印后计时)
System.out.println(end -begin); //打印排序所需时间
4.希尔排序(Shell Sort)
前提:利用插入排序中,数据越接近有序,时间效率越高
在插入排序之前做预排序(分组插排),使数据尽可能地接近有序
//定义gap进行分组排序 和插入排序相似
private static void insertSortWithGap(int[]array,int gap){
for(int i = 0;i <array.length - gap;i++){
int key = array[i+gap];
int j;
for(j = i;j>= 0 && key <array[j];j-=gap){
array[j+gap] = array[j];
}
array[j+gap] key;
}
}
public static void shellSort(int[]array){
int gap = array.length;
while(true){
gap = gap/3+1;
insertSortWithGap(array,gap); //调用insertSortWithGap
if(gap == 1){
return;
}
}
}
先处理每组的第一个数,在处理每组的下一个数。
第一组 | 第二组 | 第三组 | 第四组 |
4.1如何分组的问题:
分组分的很多:可以很快走到最后去
分组分的很多:每次分组排序后,数据接近有序
动态分组:一开始分很多组,越来越少
4.2时间复杂度
最好 | 平均 | 最坏 |
O(n) | O(n^1.3-1.4) | O(n^2) |
4.3 空间复杂度 O(1)
4.4 稳定性:不稳定(相等的两个数被分到了两个不同组,无法保证)
4.5 插入排序:直接插入排序(掌握)、折半插入排序(了解)
5.选择排序(直接选择排序/堆排序)------减治算法
思想:每次遍历无序区间(直接遍历/利用堆),找到无序区间的最大的数,把最大的数放到无序区间的最右边。一直选择n-1或n数之后,数据完全有序。
区别:直接选择排序是通过遍历来找无序区间的最大值;堆排序是通过建大堆来找无序区间的最大值
5.1直接选择排序(遍历选最大值)
public static void selectSort(int[] array){
//每次选最大的数(循环n-1次)
for(int i=0;i<array.length-1;i++){
//无序区间[0,array.length-i)
//有序区间[array.length-i,array.length]
int max = 0;
for(j=1;j<array.length-i;j++){
if(array[j]>= array[max]){
max = j;
}
}
//交换最大的数和无序区间的最后一个数
swap(array,max,array.length-i-1);
}
}
private static void swap(int[] array,int i,int j){
int t = array[i];
array[i] = array[j];
array[j] = t;
}
时间复杂度 :O(n^2) 数据不敏感
空间复杂度:O(1)
稳定性:不稳定(看经过改造是否能具有稳定性) 例如:9 5 2 3 5
5.2堆排序(选最大的数)
思想:首先将现有的元素建大堆,,然后将最大元素和最后的元素交换,在堆中去除最后一个元素,一直循环下去。
时间复杂度:O(n*log(n)) 数据不敏感
空间复杂度:O(1)
稳定性:不稳定
**选择排序选最大的,选最小的可以吗?
(1)选择排序可以同时选最大+最小,最大的往后换,最小的往前换。
(2)堆排往往建大堆,小堆更复杂,效率更低。