插入排序,对少量元素进行原位排序的有效算法。
渐近界:theta(n^2)
算法
将数组想象为两部分,已排序部分和未排序部分。开始,排序部分只包含第一个元素,余下为未排序部分。
每次,将未排序部分的第一个元素插入到已排序部分中正确的位置。
假如我们要对数组{7, -5, 2, 16, 4}排序,插入排序过程如下:
插入过程
插入算法的主要操作就是插入。即将一个值插入已排序的数组中。下面看看我们如何实现该过程。
交换
最简单的方法就是顺序往下交换,直到正确的地方。开始,待排序元素在排序部分后的第一位上。每次,待排序元素和前一位对比,如果顺序不对,就交换位置。如下:
交换方法对待排序元素有多次不必要的临时写操作,看下面一个实现,会消除这一点。
转移
仅在最后一步时执行写操作:
该方法在插入排序中最常用。
使用二分查找
使用二分查找来为插入元素寻找位置比上面的方法明显更高效。此时我们应该叫它二分插入排序。这种排序方法和上面相比,比较的次数会减少,不过整体的平均复杂度依然是theta(n^2)。从实用的角度来讲,这种提高是可以忽略的,因为插入排序本来就是用于小型数组的排序,比较的次数本就不会很多。
复杂度分析
不管插入的方式如何,插入排序的整体平均复杂度为theta(n^2)。需要的平均写操作数为theta(n^2),而比较操作数和插入方法有关,如果使用交换和转移,比较数目为theta(n^2),如果使用二分查找插入,则为theta(nlogn)。
从实用角度将,插入排序应用于小型数组(8到12)的性能比其他二次的排序算法好,如选择排序和冒泡排序。
实现
Java实现
void insertionSort(int[] arr) {
int i, j, newValue;
for (i = 1; i < arr.length; i++) {
newValue = arr[i];
j = i;
while (j > 0 && arr[j - 1] > newValue) {
arr[j] = arr[j - 1];
j--;
}
arr[j] = newValue;
}
}
C++实现
void insertionSort(int arr[], int length) {
int i, j, tmp;
for (i = 1; i < length; i++) {
j = i;
while (j > 0 && arr[j - 1] > arr[j]) {
tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
j--;
}
}
}