有序序列 r[1]~r[i-1] r[i] 无序序列 r[i+1]~r[ n]
一趟插入排序的任务:
- 在 r[1]~r[i-1] 有序序列区中查找 r[i] 的插入位置,保证 r[1...j] <= r[i] < r [j+1... i-1]。
- 将 r [j+1... i-1] 中所有的记录向后移一个位置。
- 将 r[i] 插入到 r[j+1]。
插入排序分类:
- 直接插入排序(基于顺序查找)
- 折半插入排序(基于折半查找)
- 希尔排序(基于逐趟缩小增量)
1. 直接插入排序
直接插入排序是利用顺序查找来确定 r[ i ] 在 r[ 1 ... i-1]有序序列区中的插入位置。
直接插人排序的步骤如下:
- 从 r[ i-1 ] 起向前进行顺序查找。
- 在查找过程中将小于 r[ i ] 的元素可以同时向后移动。
- 第1个元素视为已排好序的记录,从第 2 个记录开始进行插入。
package com.zth.sort;
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args){
Integer [] array1 = {3,2,5,8,4,7,6,9};
insertSort(array1);
System.out.println(Arrays.toString(array1));
Double[] array2 = {3.3,2.3,5.3,8.3,4.3,7.3,6.3,9.3};
insertSort(array2);
System.out.println(Arrays.toString(array2));
}
private static <AnyType extends Comparable<? super AnyType>>
void insertSort(AnyType[] array){
AnyType temp;
int j;
for (int i = 1; i < array.length; i++) {
temp = array[i];
for (j = i-1; j >=0 && temp.compareTo(array[j])< 0 ; j--) {
array[j+1] = array[j];
}
array[j+1] = temp;
}
}
}
算法性能分析:
时间复杂度 | 空间复杂度 | 稳定性 | ||
平均情况 | 最坏情况 | 最好情况 | ||
o(n^2) | o(n^2) | o(n) | o(1) | 稳定 |
2. 折半插入排序
对于一个有序表来说,折半查找的性能优于顺序查找,因而在 r[ 1 ]~r [ i-1 ] 这个有序序列中确定 r[ i ] 的插入位置时,可以利用折半查找实现该位置 的确定。
package com.zth.sort;
import java.util.Arrays;
public class BiInsertSort {
public static void main(String[] args){
Integer [] array1 = {3,2,5,8,4,7,6,9};
biInsertSort(array1);
System.out.println(Arrays.toString(array1));
Double[] array2 = {3.3,2.3,5.3,8.3,4.3,7.3,6.3,9.3};
biInsertSort(array2);
System.out.println(Arrays.toString(array2));
}
private static<AnyType extends Comparable<? super AnyType>>
void biInsertSort(AnyType[] array){
AnyType temp;
int j;
for (int i = 1; i<array.length; i++) {
temp = array[i];
/**
* 二分查找
*/
int low = 0;
int high = i-1;
while(low <= high){
int mid = (low + high)/2;
if (array[mid].compareTo(temp) >0){
high = mid-1;
}else {
low = mid +1;
}
}
/**
* 移动元素
*/
temp = array[i];
for ( j = i-1; j >=low; j--) {
array[j+1] = array[j];
}
array[j+1] = temp;
}
}
}
采用折半插入排序算法,可以减少关键字的比较次数。
每插入一个元素,需要比较的次数最多的情况下为折半判定树的深度。
虽然减少了比较次数,但是没有改变移动次数,所以时间复杂度仍为 o (n^2)。
空间复杂度、稳定性等都与直接插入排序一样。
3. 希尔排序
希尔排序又称为 缩小增量排序。
基本思想:
先将整个待排元素序列分割成若干个子序列(有相隔某个增量的元素组成),分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。
图片来源(http://www.cnblogs.com/jingmoxukong/p/4303279.html)
package com.zth.sort;
import java.util.Arrays;
/**
* @author 时光·漫步
* 希尔排序
*/
public class ShellSort {
public static void main(String[] args){
Integer [] array1 = {3,2,5,8,4,7,6,9};
shellSort(array1);
System.out.println(Arrays.toString(array1));
}
private static <AnyType extends Comparable <? super AnyType>>
void shellSort(AnyType[] array){
int j;
for (int gap = array.length/2; gap >0 ; gap /= 2) {
// 把距离为 gap 的元素分为一个组,扫描所有组
for (int i = gap; i < array.length; i++) {
AnyType temp = array[i];
// 对距离为 gap 的元素进行排序
for (j = i-gap; j >= 0 && temp.compareTo(array[j])< 0 ; j -= gap) {
array[j + gap] = array[j];
}
array[j + gap] = temp;
}
}
}
}
性能分析:
时间复杂度 | 空间复杂度 | 稳定性 | ||
平均情况 | 最坏情况 | 最好情况 | o(1) | 不稳定 |
o(n log n) | o(n^2) | o(n^2/3) |
4. 直接插入排序和希尔排序的比较
- 直接插入排序是稳定的;而希尔排序是不稳定的。
- 直接插入排序更适合于原始记录基本有序的集合。
- 希尔排序的比较次数和移动次数都要比直接插入排序少,当N越大时,效果越明显。
- 在希尔排序中,增量序列 gap 的取法必须满足:最后一个步长必须是 1 。
- 直接插入排序也适用于链式存储结构;希尔排序不适用于链式结构。