算法熟记-排序系列-插入排序
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. 参考资料