通常人们整理桥牌的方法是一张一张的来,将每一张牌插入到其他已经有序的牌中的适当位置。在计算机的实现中,为了给要插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位。这种算法叫做插入排序——《算法(第四版)》
插入排序思想
插入排序的思想也非常简单,即是向后遍历( i ),每次遍历的向前内循环中( j ),将a[ i ]插入到a[ i - 1],a[ i - 2],a[ i - 3 ]...中相应的位置。在这个过程之中,a[ i - 1],a[ i - 2],a[ i - 3 ]...向后挪动,a[ i ]向前。
核心代码
sort(array) {
const length = array.length;
for(let i = 1; i < length; i++){ //外层遍历i = 1开始
for(let j = i; j > 0 && this.less(array[j], array[j-1]); j--){ //向前遍历,寻找可插入位置。若array[j] < array[j - 1],则交换位置。如此,array[i-1],array[i-2],array[i-3]...中大于a[i]的则不断向后挪
this.exch(array, j, j-1);
}
}
}
此算法想要进一步优化,只需要在j层循环中,将较大的元素向后移,而不总是交换即可。如此,访问数组的次数能减半。
sort(array) {
const length = array.length;
for(let i = 1; i < length; i++){ //外层遍历i = 1开始
const iNum = array[i];
let j = i - 1;
for( ; j > 0 ; j--){
if(iNum < array[j]){ //内层遍历元素不断向右挪,直至找到可插入的位置
array[j + 1] = array[j];
}else{ //由于前面的元素已排序好,所以可以结束遍历
break;
}
}
array[j] = iNum; //插入
}
}
网上有幅gif可以更加清晰地展现此过程。
插入排序特点
综上优化后的算法,我们可以发现插入排序在已经部分有序的排序中可能非常有效,因为它能够非常快发现某个元素已经在合适的位置上。
同时也能有效地总结出以下特点。
- 插入排序所需时间取决于输入中元素的初始顺序
- 平均情况:(N^2) / 4次比较,(N^2) / 4次交换;最坏情况:(N^2) / 2次比较,(N^2) / 2次交换;最好情况:N-1次比较,0次交换
- 对部分有序的数组非常有效
总结
插入排序对于部分有序的数组非常高效,也适合于小规模数组,因为此类型数组经常在实际应用中出现
参考资料
《算法(第4版)》