众所周知,插入排序是简单排序三剑客之一 ,它也是我们平时用的最多的简单排序算法之一,今天我们就来一窥究竟。
插入排序故名思意就是插入,但是要将数插入到哪里呢?接下来来看个例子。
例:原数组{5,4,6,1,3,2}从小到大排序
第一轮 5和4比较 4小 交换 变成
4,5,6,1,3,2
第二轮 6和5比较 6大 不做操作直接进入下一轮
4,5,6,1,3,2
第三轮 1和6比较 1小 所以1和6交换位置 ,然后1在和前面的5比,比5小在交换,以此类推。
1,4,5,6,3,2
剩下两轮我就直接写答案了
第四轮
1,3,4,5,6,2
第五轮
1,2,3,4,5,6
接下来让我写代码实现下,既然又涉及到了比较和交换,我就直接拿冒泡排序那一章的的比较和交换方法了,直接放到下面了,不懂的可以去看这里https://blog.csdn.net/hyj393837/article/details/105470476。
/**
* 比较
* @param val1
* @param val2
* @return
*/
public static boolean compare(Comparable val1,Comparable val2) {
return val1.compareTo(val2) > 0;
}
/**
* 交换
* @param var
* @param i
* @param j
*/
public static void exchange(Comparable[] var,int i ,int j) {
Comparable temp = var[i];
var[i] = var[j];
var[j] = temp;
}
第一轮代码
Comparable[] var = {5,4,6,1,3,2};
//第一轮排序
for(int j=1;j>0;j--)
if(compare(var[j-1],var[j]))
exchange(var,j-1,j);
因为比较n-1轮所以直接循环n-1轮就可以了,又因为每轮开始的数组索正好是每轮的轮数 也就是j应该等于i 看下面代码
for(int i = 1;i< var.length;i++)
for(int j=i;j>0;j--)
if(compare(var[j-1],var[j]))
exchange(var,j-1,j);
看上面你已经可能发现了一个小错误,虽然并不影响了结果,但是已经影响了性能。
好了直接说了们就是 每次我们要比较的数大于已排序号数组的最大的一个数的时候,上面的代码还是去比较。
例如上面的第二轮 6已经比已排序数组中(加粗的数字)最大的值5还大,所以和5比完之后就没有必要在和4比较了,肯定会比4大,所以我们就可以直接跳出循环了
所以我们将代码整理成下面这样
for(int i = 1;i< var.length;i++)
for(int j=i;j>0 && compare(var[j-1],var[j]);j--)
exchange(var,j-1,j);
总结
1.插入排序每次交换不需要额外空间是原地排序。
2.插入排序每轮是从后往前比较,只有比它小才排到前面去,相等的还是会在后面,所以是稳定排序。
3.时间复杂度
(1)最好情况下,也就是顺序, 例如{1,2,3,4,5,6},数组不需要交换,只是遍历轮数即可 时间复杂度O(n)。
(2)最坏情况下,也就是逆序,例如{6,5,4,3,2,1},数组每轮都要交换n(不去细致推到了,反正不会超过n,应该是1~n-2,这里就取n了)次,在成于轮数,所以时间复杂度就是 O(n2)。
(3)平均情况下,我们每次插入数据的时间复杂度是O(n),因为要循环执行n次,所以就是O(n2)。
一句话总结,插入排序和冒泡排序一样,都是时间复杂度为O(n2)的原地排序算法。
思考???
这样写插入排序比以前那样写性能上有提升么?
for(int i = 1; i< var.length; i++) {
Comparable value = var[i];//要比较数据
int j = i;
for(;j > 0 && compare(var[j-1],value); j--)
var[j]=var[j-1];
var[j] = value;
}
将会在下节选择排序中解答,如果想了解优化插入排序的更多算法可以搜索希尔排序。