一.插入排序
让bound指向第一个元素,将所有元素分为[0,bound)已排序区间和[bound,size)待排序区间, 先取bound位置的元素,往前面的已排序区间中插入,插入完毕后已排序区间仍然是有序的,把bound位置的元素在前面找一个合适的位置,同时需要搬运相关元素.
接下来用Java 来实现这种算法:
public static void insertSort(int[] array) {
// 通过 bound 来划分出两个区间
// [0, bound) 已排序区间
// [bound, size) 待排序区间
for (int bound = 1; bound < array.length; bound++) {
int v = array[bound];
int cur = bound - 1; // 已排序区间的最后一个元素下标
for (; cur >= 0; cur--) {
// 注意!!!! 这个条件如果写成 >= , 咱的插入排序就不是稳定排序了
if (array[cur] > v) {
array[cur + 1] = array[cur];
} else {
// 此时说明已经找到了合适的位置
break;
}
}
array[cur + 1] = v;
}
}
插入排序性能分析:
时间复杂度:0(N^2)
空间复杂度:0(1)
稳定性: 稳定排序
插入排序两个重要特点:
1.当待排序区间元素比较少的时候,排序效率很高.
2.当整个数组比较接近有序的时候,排序效率也很高.
二.希尔排序:
进阶版本的插入排序,先分组,针对每个组进行插入排序,逐渐缩小组的个数,最终整个数组就接近有序了
9 5 2 7 3 6 8 10 7.5 12 6 4
gap=3 相当于把整个数组分成三组.每隔三个元素分为一组
第一组 :9 7 8 12
第二组: 5 3 10 6
第三组:2 6 7.5 4
先分组然后针对每一组进行插入排序.得到结果:
第一组:7 8 9 12
第二组:3 5 6 10
第四组:2 4 6 7.5
整体:
7 3 2 8 5 4 9 6 6 12 10 7.5
现在数组比之前更有序,接下来进行第二趟排序:
gap=2 把整个数组分成两组:
第一组:7 2 5 9 6 10
第二组:3 8 4 6 12 7.5
排序并合并后:
2 3 5 4 6 6 7 7.5 9 8 10 12
让gap=1 ,只剩下一组,此时一定能得到一个完整的有序结果:
2 3 4 5 6 6 7 7.5 8 9 10 12
接下来Java代码实现:
public static void shellSort(int[] array) {
int gap = array.length / 2;
while (gap > 1) {
// 需要循环进行分组插排
insertSortGap(array, gap);
gap = gap / 2;
}
insertSortGap(array, 1);
}
private static void insertSortGap(int[] array, int gap) {
// 通过 bound 来划分出两个区间
// [0, bound) 已排序区间
// [bound, size) 待排序区间
// 当吧 gap 替换成 1 的时候, 理论上这个代码就和前面的插排代码一模一样.
for (int bound = gap; bound < array.length; bound++) {
int v = array[bound];
int cur = bound - gap; // 这个操作是在找同组中的上一个元素
for (; cur >= 0; cur -= gap) {
if (array[cur] > v) {
array[cur + gap] = array[cur];
} else {
// 此时说明已经找到了合适的位置
break;
}
}
array[cur + gap] = v;
}
}
希尔排序性能分析:
时间复杂度:理论极限是0(N^1.3),如果按照 size/2,size/4,…1.
空间复杂度:0(1)
稳定性:不稳定 (分组的时候可能把相同的值分到不同组中,也就无法保证相对顺序.
三.选择排序
基于打擂台的思想,每次从数组中找出最小值,然后把最小值放到合适的位置上;
下面给定一组元素:
9 5 2 7 3 6 8
让bound=0,[0,bound)是已排序区间,[bound,size)是待排序区间
按照打擂台的方式从待排序区间中找到一个最小值放到bound位置上与擂主交换.
第一趟排序后:
2 9 5 7 3 6 8
第二趟排序后:
2 3 9 7 5 6 8
…
代码实现:
public static void selectSort(int[] array) {
for (int bound = 0; bound < array.length; bound++) {
// 以 bound 位置的元素作为擂主. 循环从待排序区间中取出元素和擂主进行比较
// 如果打擂成功, 就和擂主交换.
for (int cur = bound + 1; cur < array.length; cur++