1、对直接插入排序的优化
上一次我们说插入排序是将第i个元素插入到前i-1个有序数组的合适位置。直接插入的时候是通过不断比较和交换元素来找到这样的一个位置,而折半插入排序通过二分有序数组来确定插入位置,从而减少比较次数。
2、代码实现
public class BinInsertSort {
/**
* @param arr
* 要排序的数组
* @return 排序后的数组
*/
public static Integer[] sort(Integer[] arr) {
return sort(arr, 0, arr.length - 1);
}
public static Integer[] sort(Integer[] arr, int left, int right) {
for (int i = left + 1; i <= right; i++) {
// 当前需要进行排序的元素
Integer temp = arr[i];
// 需要比较的条件
if (arr[i - 1] > arr[i]) {
int low = left, high = i - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] < temp) {
low = mid + 1;
} else {
high = mid - 1;
}
}
// 找到合适的位置,进行元素的移动操作
for (int j = i - 1; j > high; j--) {
arr[j + 1] = arr[j];
}
arr[high + 1] = temp;
}
}
return arr;
}
}
折半插入排序所需的辅助空间与直接插入排序相同,从时间上比较,折半插入排序仅减少了元素的比较次数,但是并没有减少元素的移动次数,因此折半插入排序的时间复杂度仍为O(n^2)。
3、单元测试
/**
* 测试折半查找插入排序
*/
@Test
public void testBinInsertSort() {
int n = 10;
Integer[] arr = SortHelper.generateRandomArray(n, 0, 50);
SortHelper.printArr("排序前数组:", arr);
SortHelper.testSort(BinInsertSort.class.getName(), "sort", arr, true);
// 排序前数组:
// 29,15,32,23,7,32,28,39,28,4
// 排序后数组:
// 4,7,15,23,28,28,29,32,32,39
}
/**
* 测试选择排序、直接插入排序、折半插入排序
*/
@Test
public void testSIBSort() {
int n = 20000;
Integer[] arr = SortHelper.generateRandomArray(n, 0, 100000);// 几乎无序
Integer[] arr1 = SortHelper.generateNearlyOrderedArray(n, 100);// 近乎有序
Integer[] arr2 = SortHelper.generateRandomArray(n, 0, 10);// 存在大量重复数据
Integer[] insertSortArr = Arrays.copyOf(arr, arr.length);
Integer[] insertSortArr1 = Arrays.copyOf(arr1, arr.length);
Integer[] insertSortArr2 = Arrays.copyOf(arr2, arr.length);
Integer[] binInsertSortArr = Arrays.copyOf(arr, arr.length);
Integer[] binInsertSortArr1 = Arrays.copyOf(arr1, arr.length);
Integer[] binInsertSortArr2 = Arrays.copyOf(arr2, arr.length);
System.out.println("选择排序:");
SortHelper.testSort(SelectionSort.class.getName(), "sort", arr);
SortHelper.testSort(SelectionSort.class.getName(), "sort", arr1);
SortHelper.testSort(SelectionSort.class.getName(), "sort", arr2);
System.out.println("直接插入排序:");
SortHelper.testSort(InsertSort.class.getName(), "sort", insertSortArr);
SortHelper.testSort(InsertSort.class.getName(), "sort", insertSortArr1);
SortHelper.testSort(InsertSort.class.getName(), "sort", insertSortArr2);
System.out.println("折半插入排序:");
SortHelper.testSort(InsertSort.class.getName(), "binInsertSort", binInsertSortArr);
SortHelper.testSort(InsertSort.class.getName(), "binInsertSort", binInsertSortArr1);
SortHelper.testSort(InsertSort.class.getName(), "binInsertSort", binInsertSortArr2);
// 选择排序:
// SelectionSort->sort用时: 336ms
// SelectionSort->sort用时: 308ms
// SelectionSort->sort用时: 266ms
// 直接插入排序:
// InsertSort->sort用时: 549ms
// InsertSort->sort用时: 7ms
// InsertSort->sort用时: 369ms
// 折半插入排序:
// InsertSort->binInsertSort用时: 369ms
// InsertSort->binInsertSort用时: 7ms
// InsertSort->binInsertSort用时: 331ms
}
(1)对于近乎有序的情况,直接插入排序和折半插入排序的时间效率远远大于选择排序;
(2)对于几乎无序或者存在大量重复的数组,选择排序由于插入排序;
(3)折半插入排序的时间效率高于直接插入排序。