插入排序算法也是一个复杂度为O(n^2)的排序算法,它的原理跟打扑克类似。在打扑克时,每摸到一张牌我们都会把它插入到手上扑克中的正确位置。那么在排序中就是进行两重循环,第一重循环遍历数组,第二重循环将当前元素插入到之前元素中合适的位置。给一组图片演示加深理解
对于第一个元素因为之前没有元素,不需要插入操作。
对于第二个元素,跟前面的元素依次相比,6小于8,将6和8交换位置。下面也是同理,第三个元素依次与之前的元素比较,将其插入到合适位置。
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
insertSelection(T arr[], int n){
for(int i = 1; i < n; i++){
for(int j = i; j > 0; j--){
if(arr[j] < arr[j-1])
swap(arr[j] , arr[j-1]);
else
break;
}
}
}
对于for循环中的if判断可以放到for循环中进行
for( int j = i ; j > 0 && arr[j] < arr[j-1] ; j -- )
swap( arr[j] , arr[j-1] );
同样是O(n^2)的排序算法,但是相比较于选择排序,插入排序可以在循环中的某些情况下提前结束,所以理论上插入排序要比选择排序要快。但是上述算法在实际运行中的时间往往高于选择排序,这是因为插入排序中有许多交换操作,这种操作要比单纯的比较要耗时,所以会出现插入排序运行时间大于选择排序。
那么有没有办法减少交换操作,降低运行时间呢?答案是有的,就是利用赋值操作代替交换操作。因为交换操作进行了三次赋值,而单纯赋值只有一次,所以时间要短一点。
具体操作原理是在第二重循环时首先将当前值复制,然后与前一个数进行比较,如果比前一个值小,则将前一个值赋值到后一个位置。图形示意如下
template<typename T>
insertSelection(T arr[], int n){
for(T i = 1; i < n; i++){
int j;
T temp = arr[i];
for(j = i; j > 0 && arr[j-1] > temp; j--){
arr[j] = arr[j-1];
}
arr[j] = temp;
}
}
此时插入排序的运行时间会大幅减少,此时它的效率会高于选择排序。
同时,对于一个近乎有序的数组,插入排序算法的运行速度更加快速,这是因为对于一个近乎有序的数组,每个元素原来的位置就是正确的位置,那么就没必要进行复制操作,复杂度也就降为**O(n)**级别,这种复杂度会比后面的O(nlogn)的算法还要快,所以在某些复杂排序算法中可以使用插入排序作为子过程处理较小规模的数据。
参考:https://coding.imooc.com/learn/list/71.html