插入排序java版
基本思想:每趟将一个带排序的记录,按其关键字的大小插入到已经排好序的一组记录的适当位置上,知道所有待排序的记录全部重新插入为止。
根据查找方法的不同,分为直接插入排序、折半插入排序和希尔排序。
直接插入排序是最简单的一种,其基本操作是将一条记录插入到已经排序号的有序列表中,从而得到一个新的、记录数增1的有序表。
public class InsertSort { public static void main(String[] args) { int[] array = new int[]{2, 6, 3, 8, 9, 0, 1, 7, 4}; InsertSort(array); } public static void printArray(int[] array) { for(int i : array) { System.out.printf("%d\t", i); } System.out.println(); } public static void InsertSort(int[] array) { if( array == null) { return; } for (int i = 1; i < array.length; i++) { if( array[i] < array[i - 1]) { int key = array[i]; int j; for ( j = i - 1; j >= 0; j--) { if ( key < array[j]) { array[j + 1] = array[j]; } else { break; } } array[j + 1] = key; } printArray(array); } } }
时间复杂度: O(n2)
空间复杂度:O(1)
算法特点:
- 稳定排序
- 算法简单,容易实现
- 适用于与链式表结构
- 更适合初始记录基本有序的情况,当初始记录无序且n较大时,此算法时间复杂度高,不宜采用。
折半插入排序,用折半查找法来查找当前记录在以排好的序列中的插入位置,由此进行的插入排序称之为折半插入排序
public class BInsertSort { public static void main(String[] args) { int[] array = new int[]{2, 6, 3, 8, 9, 0, 1, 7, 4}; BInsertSort(array); } public static void BInsertSort(int[] array) { printArray(array); for (int i = 1; i < array.length; i++) { int key = array[i]; int low = 0, high = i - 1; while(low <= high) { int mod = (low + high) / 2; if (key < array[mod]) { high = mod - 1; } else { low = mod + 1; } } for (int j = i-1; j >= high + 1; j--) { array[j+1] = array[j]; } array[high + 1] = key; printArray(array); } } public static void printArray(int[] array) { for(int i : array) { System.out.printf("%d\t", i); } System.out.println(); } }
算法时间复杂度:从时间上比较,折半查找比顺序查找快,所以就平均性能来说,折半插入排序优先于直接插入排序,在平均情况下,折半插入排序仅减少了关键字间的比较次数,而记录的移动次数不变,因此,折半插入排序的事件复杂度任为O(n2)
空间复杂度:折半插入排序所需附加存储空间和直接插入排序相同,只需要一个记录的辅助空间,所以空间复杂度任为O(n2)
算法特点:
- 稳定排序
- 因为进行折半查找,所以只能用于顺序结构,不能用于链式结构
- 适合初始记录无序,n较大时的情况
希尔排序又称缩小增量排序,是插入排序的一种。直接插入排序,当待排序的记录个数较少而且排序序列基本有序时,效率较高。希尔排序基于这两点,对直接插入排序进行改进。
希尔排序实质是采用分组插入的方法,先将整个待排序的记录序列分割成几组,从而减少参与直接插入排序的数据量,对每组分别进行直接插入排序,然后增加每组的数据量,重新分组。这样经过几次分组排序后,整个序列中的记录“基本有序”时,再对全体记录进行一次插入排序。
希尔对记录的分组,不是简单地“逐段分割”,而是将相隔某个“增量”的记录分为一组。算法步骤如下:
- 第一趟取增量d1(d1 < n) 把全部记录分成d1组,所有间隔为d1的记录分在同一组,在各个组中进行直接插入排序。
- 第二趟取增量d2(d2 < d1),重复上诉的分组和排序。
- 依次类推直到增量等于1,所有记录在同一组中进行直接插入排序为止。
public static void ShellSort(int[] array, int dk) { for(int i = dk; i < array.length; i+= 1) { if( array[i] < array[i - dk]) { int key = array[i]; int j; for ( j = i - dk; j >= 0; j -= dk) { if ( key < array[j]) { array[j + dk] = array[j]; } else { break; } } array[j + dk] = key; } printArray(array); } } public static void ShellSort(int[] array, int[] dt) { printArray(array); for(int d : dt) { ShellSort(array, d); } }
时间复杂度:O(n3/2)
空间复杂度:O(1)
算法特点:
- 记录跳跃式移动导致排序方法是不稳定的。
- 只能用于顺序结构,不能用于链式结构。
- 增量序列可以有各种取法,但应该是增量序列中的值没有除1之外的公因子,并且最后一个增量值必然等于1.
- 记录总的比较次数和移动次数都不比直接插入排序要少,n越大,效果越明显。所以适合初始记录无序,n较大时的情况。