插入排序顾名思义,应该是在有序的序列中插入一个数据,并经过一系列变化,成为一个新的有序的序列。像是在玩扑克牌时,抓到的第一张牌是5,放在手里,就是一个有序的序列;抓到的第二张牌是3,则将3放在5的右面,又形成了一个有序的序列;第三张抓到4,则从手上最后一张开始比较,4比3大,则4应该在3的左边,再和5比较,4比5小,则4应该在5的右边,至此把4插入到5和3之间,形成一个新的有序的序列。
时间复杂度:O(n^2)
虽然插入排序是个O(n^2)级别的算法,但在某些情况下,他甚至比O(nlogn)级别的算法还要快。在下面的代码实现中会提到。
稳定性:稳定
代码实现:
按照上面的描述,先实现一个简单的方法
template <typename T>
void insertSort(T arr[],int n)
{
for (int i = 1; i < n;i++)
{
for (int j = i; j> 0 && arr[j] < arr[j - 1]; j--)
{
swap(arr[j], arr[j - 1]);
}
}
}
这里思考一下这个过程有没有更好的办法?
上面实现的代码转变成起牌的过程就是:假如手上从左至右依次拿着9、8、6、5、4,这时我们摸到了一张7:
首先与4比较,7>4,交换4和7的位置。
-当前序列为:8、6、5、7、4
我们实际上相当于把7先插入了5和4之间,这就有问题了。我们现实中玩牌,都是从右开始比较,直到找到正确的位置才插入,我们上面写的代码势必会造成不必要的多次赋值。
下面是我依照现实情况做的优化:
template<typename T>
void insertSort(T arr[], int n)
{
for (int i = 1; i < n; i++)
{
T e = arr[i];
int j;
for (j = i; j > 0 && arr[j - 1] > e; j--)
{
arr[j] = arr[j - 1];
}
arr[j] = e;
}
}
这个实现算法在数组基本有序的情况下,性能好到爆炸。
测试性能
Test1
数据量:100000
数据关系:完全随机
测试的算法:插入排序两个版本
Test2
数据量:1000000
数据关系:基本有序
测试的算法:插入排序两个版本
Test3
数据量:100000
数据关系:含有大量重复元素
测试的算法:插入排序两个版本
结果大家自己体会一下,哈哈