插入排序分为 直接插入排序 和优化后的 折半插入排序 与 希尔排序,通常所说的是直接插入排序。
一、直接插入
原理
通过构建有序序列,对于未排序的数据,在已排序序列中从后向前扫描,找到相应位置并插入。
步骤
- 从第一个元素开始,该元素认为是已经被排过序的
- 取出下一个元素,在已排序的元素序列中从后向前做对比
- 如果对比的元素(已排序)大于新元素,将该元素移到下一个位置,也就是往后移一位
- 重复步骤 3 ,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入到该位置
- 重复步骤2 ~ 5
实现
// 插入排序
const insertSort = array => {
let len = array.length
if (len<1) return
let preIndex, current;
for (let i = 1; i<len; i++){
preIndex = i - 1 // 待比较元素的标
current = array[i] //当前元素
while (preIndex >= 0 && array[preIndex] > current){
array[preIndex+1] = array[preIndex] //将待比较元素后移一位
preIndex -- //游标前移一位
}
if (preIndex + 1 != i){
//避免同一个元素赋值给自身
array[preIndex + 1] = current //将当前元素插入预留空位
}
}
return array
}
二、折半插入
原理
折半插入排序是直接插入排序的升级版,鉴于插入排序第一部分为已排好序的数组,我们不必按顺序依次寻找插入点,只需要比较他们的中间值与带插入元素的大小即可。
步骤
- 取0 ~ i-1 的中间点( m = (i-1) >> 1,有符号右移运算,等于 Math.floor((i-1)/2 ),array[i] 与 array[m] 进行比较,若 array[i] > array[m],则说明待插入的元素在 m ~ i-1 索引之间,反之在0 ~ m 之间。
- 重复步骤 1,每次缩小一半的查找范围,直至找到插入的位置
- 将数组中插入位置之后的元素全都后移一位
- 在指定位置插入第 i 个元素(即待插入的元素)。
注:x >> 1 是位运算中的右移运算,表示右移一位,等同于 x 除以 2 再取整,即 x >> 1 == Math.floor(x/2) 。 x<<1等于x*2
// 这般插入排序
cosnt binaryInsertSort = array => {
const len = array.length;
if (len <= 1) return;
let current, i, j, low, high, m;
for (i = 1; i < len; i++) {
low = 0;
high = i - 1;
current = array[i];
while (low <= high) {
//步骤 1 & 2 : 折半查找
m = (low + high) >> 1; // 注: x>>1 是位运算中的右移运算, 表示右移一位, 等同于 x 除以 2 再取整, 即 x>>1 == Math.floor(x/2) .
if (array[i] >= array[m]) {
//值相同时, 切换到高半区,保证稳定性
low = m + 1; //插入点在高半区
} else {
high = m - 1; //插入点在低半区
}
}
for (j = i; j > low; j--) {
//步骤 3: 插入位置之后的元素全部后移一位
array[j] = array[j - 1];
console.log('array2 :', JSON.parse(JSON.stringify(array)));
}
array[low] = current; //步骤 4: 插入该元素
}
console.log('array2 :', JSON.parse(JSON.stringify(array)));
return array;
}