插入排序(Insertion Sort):是一种简单的排序算法,也是一种稳定的排序算法。其实现原理是:从待排序序列中取出一个值,将其与已排序序列中的值进行比较后插入到合适的位置;重复此步骤,直到待排序序列中不再有值。
插入排序的示例:
假设待排序序列为 5、1、4、2、8,如果采用选择排序对其进行升序排序,则整个排序过程如下所示:
- 第 0 个位置的 5 默认是有序的。
-
从第 1 个位置取出 1;和 5 进行比较,1 小于 5,5 向后位移一位;已经到达了已排序序列的最左边,0 插入到第 1 个位置。
-
从第 2 个位置取出 4; 和 5 进行比较,4 小于 5,5 向后位移一位;和 1 进行比较,4 大于 1;0 插入到 1 后面的位置。
-
从第 3 个位置取出 2; 和 5 进行比较,2 小于 5,5 向后位移一位;和 4 进行比较,2 小于 4,4 向后位移一位;和 1 进行比较,2 大于 1;0 插入到 1 后面的位置。
-
从第 4 个位置取出 8; 和 5 进行比较,8 大于 5;8 插入到 5 后面的位置。
插入排序的效率:
插入排序比冒泡排序、选择排序效率都高,是简单排序中效率最高的。
比较次数:
假设有 n 个数据项,第一趟最多需要比较 1 次,第二趟最多需要比较 2 次,以此类推,最后一趟最多需要比较 n - 1 次,因此插入排序真实的最多比较次数是 1 + 2 + 3 + ... + n - 1 = n*(n -1) / 2
,但是平均只有数据项的一半需要进行比较,因此插入排序真实的平均比较次数是 n*(n -1) / 4
n * (n - 1) / 4 = n² / 4 - n / 4
,根据推导大 O 表示法的规则 2 只保留最高阶项,变为 n² / 4
,根据规则 3 去除最高阶项的常量,变为 n²
,因此选择排序的比较次数的大 O 表示法为 O(n²)
。
复制次数:
假设有 n 个数据项,第一趟最多需要复制 1 次,第二趟最多需要复制 2 次,以此类推,最后一趟最多需要复制 n - 1 次,因此插入排序真实的最多复制次数是 1 + 2 + 3 + ... + n - 1 = n*(n -1) / 2
,但是平均只有数据项的一半需要进行复制,因此插入排序真实的平均复制次数是 n*(n -1) / 4
n * (n - 1) / 4 = n² / 4 - n / 4
,根据推导大 O 表示法的规则 2 只保留最高阶项,变为 n² / 4
,根据规则 3 去除最高阶项的常量,变为 n²
,因此选择排序的复制次数的大 O 表示法为 O(n²)
。
插入排序的代码实现:
步骤:把第一个元素看作已排序序列,把第二个元素到最后一个元素看作待排序序列。从左到右依次扫描待排序序列,将扫描到的每个元素和已排序序列中的元素进行比较后插入到合适的位置。
// 选择排序:以升序为例
ArrayList.prototype.insertionSort = function () {
// 1. 获取列表长度
var length = this.array.length
// 2. 外层循环:将第 0 个位置的元素看作局部有序,从第 1 个位置的元素开始,和前面局部有序的元素进行比较,并在合适的位置插入
for (var i = 1; i < length ; i++) {
// 3. 取出 i 位置的元素,保存在变量 temp 中
var temp = this.array[i]
// 4. 内层循环:只要 temp 小于局部有序中的元素,就将此位置的元素向后位移一位,进行下一次遍历比较,直到找到 temp 大于局部有序中的元素,退出循环
// 比较 temp 和它的前一个位置 j - 1 的元素,如果 temp 小,则将 j - 1 位置的元素向后移动一位,并且 j-- 接着遍历已排序序列中的前一个元素
var j = i
while(temp < this.array[j - 1] && j > 0) {
this.array[j] = this.array[j - 1]
j--
}
// 5. 将 temp 放置在小于它的元素的后面,this.array[j - 1 + 1]
this.array[j] = temp
}
}
// 测试插入排序
list.insertionSort()
console.log(list.toString()) // 1 2 4 5 8