一. 选择排序
function insertionSort(arr) {
const len = arr.length;
// 从数组的第二个元素开始,因为第一个元素默认已排序
for (let i = 1; i < len; i++) {
// 取出当前要插入的元素,就像从桌上拿起一张牌
let key = arr[i];
// 已排序部分的最后一个元素的索引
let j = i - 1;
// 在已排序部分从后往前比较,找到合适的插入位置
while (j >= 0 && key < arr[j]) {
// 如果当前元素大于 key,就把它往后挪一个位置,就像把牌往后挪
arr[j + 1] = arr[j];
// 继续往前比较
j--;
}
// 插入元素,把牌放到合适的位置
arr[j + 1] = key;
}
return arr;
}
// 测试数组
const array = [5, 3, 8, 4, 2];
const sortedArray = insertionSort(array);
console.log(sortedArray);
思路:
类似打扑克牌
- 先取第一张,然后是后面几张牌;
- 后面几张牌,从左至右,先取左边第一张;
- 将左边第一张和取出的第一张比较,根据大小比较,确定是否交换顺序;
- 已经排序的有两条数据,再取后面的数据,从左至右,取左边第一张;
- 将取出的第一张和已排序的数据比较,从右至左,先和已排序的最右边的数据比较,比较大小,确定是否交换顺序,如果不需要交换,则停止,如果需要交换 , 则继续从左去比较数据
二.总结
插入排序(Insertion Sort)是一种简单直观的排序算法,其工作原理与整理扑克牌的方式类似。下面从算法原理、排序步骤、代码实现、复杂度分析、稳定性等多个方面详细介绍插入排序。
算法原理
插入排序将数组分为已排序和未排序两部分。初始时,已排序部分仅包含数组的第一个元素,其余元素构成未排序部分。之后,依次从未排序部分取出元素,将其插入到已排序部分的合适位置,使得插入后已排序部分仍然保持有序,不断重复该过程,直至整个数组有序。
排序步骤
假设我们要对数组 [5, 3, 8, 4, 2] 进行升序排序,插入排序的具体步骤如下:
初始状态
将数组的第一个元素 5 视为已排序部分,[3, 8, 4, 2] 为未排序部分。此时已排序数组为 [5],未排序数组为 [3, 8, 4, 2]。
第一轮插入
- 从未排序部分取出第一个元素
3。 - 将
3与已排序部分的元素从后往前比较,即先与5比较,发现3 < 5。 - 把
5往后移动一位,将3插入到原来5的位置。此时已排序数组变为[3, 5],未排序数组变为[8, 4, 2]。
第二轮插入
- 取出未排序部分的第一个元素
8。 - 将
8与已排序部分的元素从后往前比较,先与5比较,8 > 5,所以8直接放在5后面。此时已排序数组变为[3, 5, 8],未排序数组变为[4, 2]。
第三轮插入
- 取出未排序部分的第一个元素
4。 - 将
4与已排序部分的元素从后往前比较,先与8比较,4 < 8,把8往后移动一位;再与5比较,4 < 5,把5往后移动一位;接着与3比较,4 > 3,将4插入到3后面。此时已排序数组变为[3, 4, 5, 8],未排序数组变为[2]。
第四轮插入
- 取出未排序部分的最后一个元素
2。 - 将
2与已排序部分的元素从后往前比较,依次将8、5、4、3往后移动一位,最后将2插入到最前面。此时整个数组排序完成,变为[2, 3, 4, 5, 8]。
算法特点
-
时间复杂度:
- 最好情况:O(n)(数组已经有序)
- 最坏情况:O(n²)(数组逆序)
- 平均情况:O(n²)
-
空间复杂度:O(1)(原地排序,只需要常数级别的额外空间)
-
稳定性:插入排序是稳定的排序算法,即相同关键字的元素在排序后相对位置不变。
-
适用性:对于小规模数据或基本有序的数据,插入排序性能较好。然而,对于大规模无序数据,其性能较差。
难点总结
arr[j+1]=current代码解析; 有两种情况,一种是一直循环,直到j=-1,这时赋值arr[0]=current即可,另一种是没有轮询到j=-1提前结束,比如轮询到j的时候,发现不需要替换,但是上一步j+1位置已经赋值并预留了替换的位置了,上一步j+1的值已经后移到j+2的位置了,所以arr[j+1]赋值current就可以了