1. 简述
假设待排序数组为 int array[], 数组长度为n。
第1趟,认为array[0]-array[0]已经排序,把array[1]插入到合适的位置。
第2趟,认为array[0]-array[1]已经排序,把array[2]插入到合适的位置。
···
第n-1趟,认为array[0]-array[n-2]已经排序,把array[n-1]插入到合适的位置。
2. 复杂度
最好的时间复杂度是O(n),对已经排序好的数组,只需要n-1次比较就可以了。平均时间复杂度和最坏时间复杂度都是O(n^2)。
在每次趟中,寻找合适位置时,可以使用二分查找的方法,来减少比较次数。
稳定性上,如果不使用二分查找,那么是稳定的排序,否则是不稳定的排序。
3. 代码
void
insertion_sort(
int
array[],
int
n) {
int pos, left, right, mid, tmp;
for ( int i = 1 ; i < n; i ++ ) {
if (array[i] >= array[i - 1 ]) { // 不需要插入
continue ;
}
else if (array[i] < array[ 0 ]) { // 插入到最前面
pos = 0 ;
}
else { // 使用二分查找,来确定插入位置
left = 0 ; right = i - 1 ;
while (right - left > 1 ) {
mid = (left + right) / 2 ;
if (array[i] < mid) right = mid;
else left = mid;
}
pos = right;
}
// 插入过程
tmp = array[i];
for ( int j = i; j > pos; j -- )
array[i] = array[i - 1 ];
array[pos] = tmp;
}
}
int pos, left, right, mid, tmp;
for ( int i = 1 ; i < n; i ++ ) {
if (array[i] >= array[i - 1 ]) { // 不需要插入
continue ;
}
else if (array[i] < array[ 0 ]) { // 插入到最前面
pos = 0 ;
}
else { // 使用二分查找,来确定插入位置
left = 0 ; right = i - 1 ;
while (right - left > 1 ) {
mid = (left + right) / 2 ;
if (array[i] < mid) right = mid;
else left = mid;
}
pos = right;
}
// 插入过程
tmp = array[i];
for ( int j = i; j > pos; j -- )
array[i] = array[i - 1 ];
array[pos] = tmp;
}
}
实际上,插入排序的元素赋值操作往往是比较耗时的,二分的方法只能减少一些比较的次数,并不能减少元素移动的次数,因此对于性能提升不是很多。下面给出一个比较“简化”的插入排序代码:
void
insertion_sort(
int
array[],
int
n) {
int temp,i,j;
for (i = 1 ; i < n; i ++ ) {
temp = array[i];
for (j = i; j > 0 && temp < array[j - 1 ]; j -- ) { // 此时array[j]==temp
array[j] = array[j - 1 ];
}
array[j] = temp;
}
}
int temp,i,j;
for (i = 1 ; i < n; i ++ ) {
temp = array[i];
for (j = i; j > 0 && temp < array[j - 1 ]; j -- ) { // 此时array[j]==temp
array[j] = array[j - 1 ];
}
array[j] = temp;
}
}
上面代码举个例子来说明一下,比如对1 3 4 5 6 7 2,现在要把最后的2插入到前面有序的数组中了,首先,temp=2,然后就用temp与前面的这些数字逐个比较,如果temp更小,那么就把前面的数字后移一位,直到结束,在把temp放到空出的位置。对于例子就是在循环结束时,1 3 3 4 5 6 7 ,temp=2, j=1,然后array[1]=2,得到1 2 3 4 5 6 7
4. 参考资料