折半插入排序(Binary Insertion Sort)是插入排序算法的一种优化版本。插入排序的基本思想是将一个记录插入到已经排序好的有序表中,从而得到一个新的、记录数增加1的有序表。传统的插入排序在寻找插入位置时,采用的是顺序比较的方式,即逐个与有序表中的元素进行比较,直到找到比待插入元素大的元素或达到有序表的末尾。而折半插入排序则是在这个查找过程中使用了二分查找(Binary Search)的思想,从而减少了比较的次数,提高了排序的效率。
折半插入排序算法思想
-
初始化:假设数组的第一个元素是已经排序好的,因此从第二个元素开始遍历数组。
-
二分查找插入位置:对于每个待排序的元素(从第二个元素开始),使用二分查找的方法在已经排序好的数组部分中查找该元素的正确插入位置。即,在已排序序列中从后往前(或从前往后,取决于具体实现)使用二分查找法找到第一个不小于(或大于)待插入元素的位置。
-
插入元素:找到插入位置后,将该位置及之后的所有元素都向后移动一位,为新元素腾出空间,然后将新元素插入到找到的位置。
-
重复:重复步骤2和3,直到遍历完整个数组。
折半插入排序的特点
- 优点:
- 相较于传统的插入排序,折半插入排序在查找插入位置时采用了二分查找,减少了比较次数,提高了效率。
- 折半插入排序仍然是稳定的排序算法,即相等的元素在排序后的相对位置不会改变。
- 缺点:
- 虽然查找插入位置时使用二分查找减少了比较次数,但元素移动的次数并未减少,这仍然是插入排序的一个主要瓶颈。
- 对于数据量很大的情况,折半插入排序的性能仍然不够高效,特别是对于已经接近有序的数组,其效率提升并不明显。
适用场景
折半插入排序适用于数据量不大,且数据基本有序的情况。在数据量较小时,其效率可以接近甚至超过一些更复杂的排序算法,如快速排序、归并排序等。但在数据量较大时,由于其元素移动的开销较大,性能可能不如其他排序算法。
Java代码实现
import java.util.Arrays;
/*
有序区内
arr[high]以及之前的元素都小于等于element
从arr[low]及以后的元素都大于element
*/
public class BinaryInsertSort {
public static void main(String[] args) {
int[] arr = {5, 3, 8, 6, 2, 7, 1, 4};
binaryInsertSort(arr);
}
private static void binaryInsertSort(int[] arr) {
// i代表待插入元素的索引
for (int i=1;i<arr.length;i++){
// 待排的素
int element=arr[i];
// 已排序区域的最后元素索引
int low=0;
int high=i-1;
// 如果待插入元素比已排序区的最后一个元素大则不用管直接进行下一轮插入
if(element>arr[high]){
continue;
}
//
while(low<=high){
int mid=(low+high)/2;
if(element<arr[mid]){
high=mid-1;
}else {
low=mid+1;
}
}
// 有序区内
// arr[high]以及之前的元素都小于等于element
if (high < 0) {
// 如果 high < 0,说明所有已排序的元素都大于或等于 element
// 将 element 插入到数组的开始位置
for (int j = i; j > 0; j--) {
arr[j] = arr[j - 1];
}
arr[0] = element;
} else {
// 从已排序的最后一个元素开始到arr[high+1]依次往后移
for (int j = i - 1; j >= high + 1; j--) {
arr[j + 1] = arr[j];
}
arr[high + 1] = element;
}
System.out.println(Arrays.toString(arr));
}
}
}