说到前面的小小基础:什么是稳定性?
两个相等的数据通过排序之后,相对位置不发生变化,则称该种排序具有稳定性,是稳定的!
插入排序
基本思想:将整个数据分为有序区间和无序区间,每次选择无序区间的第一个数据插入到有序区间的合适的位置。
举个栗子!!
初始数据:3 2 5 7 6 3 4 9
前部分为有序区间,后部分为无序区间。
排序结果为:2 3 5 7 6 3 4 9
(省略……)
再次排序为:2 3 5 6 7 3 4 9
再次排序为:2 3 3 5 6 7 4 9
最后排序结果为:2 3 3 4 5 6 7 9
以上,两个相同数据3的位置通过插入排序能确保相对位置不变,所以插入排序具备稳定性
public static void insertSort(long[] array) {
for (int i = 0; i < array.length - 1; i++) {
// 有序 [0, i]
// 无序 [i + 1, array.length)
// 无序区间中的第一个数据是 [i + 1]
long key = array[i + 1]; //无序区间的第一个元素
// 依次在有序区间进行比较 [i, 0]
int j;
for (j = i; j >= 0; j--) {
// [j] 就是需要和 key 进行比较的数
if (key < array[j]) {
array[j + 1] = array[j];
} else {
break;
}
}
array[j + 1] = key;
}
}
时间复杂度最好情况下为:O(n) //数据有序的情况下
时间复杂度最坏情况下为:O(n^2) //数据逆序的情况下
时间复杂度平均情况下为:O(n^2)
空间复杂度为:O(1) 稳定!
希尔排序
又称“缩小增量法”,基本思想:选定一个整数,把待排序部分分成gap个小组,每个间距为n的数据为一组,按小组排序。将gap越取越小,直到gap==1,此时排序完成。
举个栗子!!
初始数据为:9 2 3 6 4 7 8 1 5 3;//令前面的3为a,后面的3为b
当gap==5时,9 2 3 6 4 7 8 1 5 3; (分为五组,每个颜色表示一组)
按小组排序之后结果为:7 2 1 5 3 9 8 3 6 4;(再将其分为3组)//此时b在a之前
gap==3时,7 2 1 5 3 9 8 3 6 4;
按小组排序之后为:4 2 1 5 3 6 7 3 9 8;(分为两组)//此时b在a之前
gap==2时,4 2 1 5 3 6 7 3 9 8;
按小组排序之后结果为:1 2 3 3 4 5 7 6 9 8;//此时b在a之前
此时gap==1时,作出排序为:1 2 3 3 4 5 6 7 8 9;//此时b在a之前
以上,两个相同数据3的位置通过希尔排序并不能确保相对位置不变,所以希尔排序不具备稳定性
public static void shellSort(long[] array){
int gap = array.length/2;
while(gap>=1){ //循环截止条件:gap<1
insertSortGap(array,gap);
if(gap==1){ //gap==1时,排序完成
break;
}
gap = gap/2; //将gap逐渐减小,即将分组逐渐扩大
}
}
private static void inserSortGap(long[] array,int gap){
for(int i = gap;i<array.length;i++){
long key = array[i];
int j;
for(j=i-gap;j>=0;j=j-gap){
if(key<array[j]){
array[j+gap] = array[j];
}else{
break;
}
}
array[j+gap] = key;
}
}
时间复杂度最好情况下为:O(n) //数据有序的情况下
时间复杂度最坏情况下为:O(n^2)
时间复杂度平均情况下为:O(n^1.3)
空间复杂度为:O(1) 不稳定!
选择排序
将数据分为有序区间和无序区间,每次选出无序区间的最大值,存放在有序区间的最后(或最前),直到无序区间内再无数据。
举个栗子!!
初始数据:9 2 3 6 4 7 8 1 5 3
前部分为无序,后部分为有序,则:2 3 6 4 7 8 1 5 3 9
继续排序:2 3 6 4 7 1 5 3 8 9
继续排序:2 3 6 4 1 5 3 7 8 9
(此处省略,以此类推……)
当排序到数据3时,相对位置靠前的3将被放置在有序区间的最前面,所以两个数据的相对位置会发生改变,所以选择排序不具备稳定性
public static void selectSort(long[] array) {
// 一共多少次选择的过程
for (int i = 0; i < array.length - 1; i++) {
// 无序区间: [0, array.length - i)
// 有序区间: [array.length - i , array.length)
int maxIndex = 0; // <-- 假设最大的数就是 array[0]
for (int j = 1; j < array.length - i; j++) {
if (array[j] > array[maxIndex]) {
maxIndex = j;
}
}
// 期望 maxIndex 指向无序区间的最大的数的下标
swap(array, maxIndex, array.length - i - 1);
}
}
时间复杂度为:O(n^2)
空间复杂度为:O(1) 不稳定!
堆排序
基本原理也是选择排序,不通过遍历找到区间最大值,而是通过堆来找到区间最大值
ps:升序建大堆,降序建小堆!!!
public static void heapSort(long[] array) {
// 1. 建大堆
createHeap(array, array.length);
// 2. 进行选择的过程,一共需要 array.length - 1 组
for (int i = 0; i < array.length - 1; i++) {
// 无序:[0, array.length - i)
swap(array, 0, array.length - i - 1);
// 无序:[0, array.length - i - 1)
adjustDown(array, array.length - i - 1, 0);
}
}
private static void adjustDown(long[] array, int size, int index) {
while (2 * index + 1 < size) {
int maxIndex = 2 * index + 1;
if (maxIndex + 1 < size && array[maxIndex + 1] > array[maxIndex]) {
maxIndex++;
}
if (array[index] >= array[maxIndex]) {
break;
}
swap(array, index, maxIndex);
index = maxIndex;
}
}
private static void createHeap(long[] array, int size) {
for (int i = (size - 2) / 2; i >= 0; i--) {
adjustDown(array, size, i);
}
}
时间复杂度为:O(n*log(n))
空间复杂度为:O(1) 不稳定!
冒泡排序
基本思路,通过相邻数的比较,将最大数冒泡到无序区间的最后面,持续这个过程,直到数据有序
public static void bubbleSort(long[] array) {
// 需要多少次冒泡过程
for (int i = 0; i < array.length - 1; i++) {
// 无序: [0, array.length - i)
// 有序: [array.length - i, array.length)
// 每次冒泡之前,假设数组已经有序
boolean isSorted = true;
// 进行冒泡过程
for (int j = 0; j < array.length - i - 1; j++) {
if (array[j] > array[j + 1]) {
swap(array, j, j + 1);
isSorted = false;
}
}
if (isSorted) {
break;
}
}
}
时间复杂度最好情况下为:O(n) //数据有序的情况下
时间复杂度最坏情况下为:O(n^2) //数据逆序的 情况下
时间复杂度平均情况下为:O(n^2)
空间复杂度为:O(1) 稳定!
快速排序和归并排序
请看下集!