思路:插入排序的核心思想是“找到元素在它前面那个序列中的正确位置”。
具体来说,插入排序所有的操作都基于一个这样的前提:当前元素前面的序列是有序的。基于这个前提,从后往前去寻找当前元素在前面那个序列里的正确位置。
为了更好的理解插入排序,可以看看插入排序的动画:https://visualgo.net/zh/sorting
插入排序里的几个关键点:
- 当前元素前面的那个序列是有序的
- “正确的位置”如何定义——所有在当前元素前面的数都不大于它,所有在当前元素后面的数都不小于它
- 在有序序列里定位元素位置的时候,是从后往前定位的。只要发现一个比当前元素大的值,就需要为当前元素腾出一个新的坑位。
基于这个思路,我们来写代码:
function insertSort(arr) {
// 缓存数组长度
const len = arr.length
// temp 用来保存当前需要插入的元素
let temp
// i用于标识每次被插入的元素的索引
for(let i = 1;i < len; i++) {
// j用于帮助 temp 寻找自己应该有的定位
let j = i
temp = arr[i]
// 判断 j 前面一个元素是否比 temp 大
while(j > 0 && arr[j-1] > temp) {
// 如果是,则将 j 前面的一个元素后移一位,为 temp 让出位置
arr[j] = arr[j-1]
j--
}
// 循环让位,最后得到的 j 就是 temp 的正确索引
arr[j] = temp
}
return arr
}
插入排序的时间复杂度
- 最好时间复杂度:它对应的数组本身就有序这种情况。此时内层循环只走一次,整体复杂度取决于外层循环,时间复杂度就是一层循环对应的 O(n)。
- 最坏时间复杂度:它对应的是数组完全逆序这种情况。此时内层循环每次都要移动有序序列里的所有元素,因此时间复杂度对应的就是两层循环的 O(n^2)
- 平均时间复杂度:O(n^2)