手写插入排序算法
实现插入排序算法,理解其原理和应用场景
问题
插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
需要实现一个插入排序函数,能够对数组进行升序排序。
解答
/**
* 插入排序
* @param {number[]} arr - 待排序的数组
* @return {number[]} - 排序后的数组
*/
function insertionSort(arr) {
// 创建数组副本,避免修改原数组
const result = [...arr];
const len = result.length;
// 从第二个元素开始遍历(第一个元素默认已排序)
for (let i = 1; i < len; i++) {
// 保存当前要插入的元素
const current = result[i];
// 已排序序列的最后一个元素的索引
let j = i - 1;
// 在已排序序列中从后向前扫描,找到插入位置
while (j >= 0 && result[j] > current) {
// 将大于 current 的元素向后移动一位
result[j + 1] = result[j];
j--;
}
// 插入元素到正确位置
result[j + 1] = current;
}
return result;
}
/**
* 原地插入排序(直接修改原数组)
* @param {number[]} arr - 待排序的数组
* @return {number[]} - 排序后的数组
*/
function insertionSortInPlace(arr) {
const len = arr.length;
for (let i = 1; i < len; i++) {
const current = arr[i];
let j = i - 1;
while (j >= 0 && arr[j] > current) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = current;
}
return arr;
}
/**
* 支持自定义比较函数的插入排序
* @param {Array} arr - 待排序的数组
* @param {Function} compareFn - 比较函数
* @return {Array} - 排序后的数组
*/
function insertionSortWithCompare(arr, compareFn = (a, b) => a - b) {
const result = [...arr];
const len = result.length;
for (let i = 1; i < len; i++) {
const current = result[i];
let j = i - 1;
// 使用自定义比较函数
while (j >= 0 && compareFn(result[j], current) > 0) {
result[j + 1] = result[j];
j--;
}
result[j + 1] = current;
}
return result;
}
使用示例
// 示例1:基本使用
const arr1 = [5, 2, 8, 1, 9, 3];
console.log(insertionSort(arr1));
// 输出: [1, 2, 3, 5, 8, 9]
// 示例2:已排序数组
const arr2 = [1, 2, 3, 4, 5];
console.log(insertionSort(arr2));
// 输出: [1, 2, 3, 4, 5]
// 示例3:逆序数组
const arr3 = [5, 4, 3, 2, 1];
console.log(insertionSort(arr3));
// 输出: [1, 2, 3, 4, 5]
// 示例4:包含重复元素
const arr4 = [3, 1, 4, 1, 5, 9, 2, 6];
console.log(insertionSort(arr4));
// 输出: [1, 1, 2, 3, 4, 5, 6, 9]
// 示例5:降序排序
const arr5 = [5, 2, 8, 1, 9];
console.log(insertionSortWithCompare(arr5, (a, b) => b - a));
// 输出: [9, 8, 5, 2, 1]
// 示例6:对象数组排序
const students = [
{ name: 'Alice', score: 85 },
{ name: 'Bob', score: 92 },
{ name: 'Charlie', score: 78 }
];
console.log(insertionSortWithCompare(students, (a, b) => a.score - b.score));
// 输出: [{ name: 'Charlie', score: 78 }, { name: 'Alice', score: 85 }, { name: 'Bob', score: 92 }]
// 示例7:原地排序
const arr6 = [3, 1, 4, 1, 5];
insertionSortInPlace(arr6);
console.log(arr6);
// 输出: [1, 1, 3, 4, 5](原数组被修改)
关键点
-
思想:将数组分为已排序和未排序两部分,每次从未排序部分取出一个元素,插入到已排序部分的正确位置
-
双层循环:外层循环遍历未排序元素,内层循环在已排序序列中找到插入位置
-
元素移动:通过 while 循环将大于当前元素的已排序元素依次后移,为插入腾出空间
-
时间复杂度:
- 最好情况:O(n) - 数组已排序,只需遍历一次
- 平均情况:O(n²)
- 最坏情况:O(n²) - 数组逆序
-
空间复杂度:O(1) - 只需要常数级额外空间
-
稳定性:插入排序是稳定的排序算法,相等元素的相对位置不会改变
-
适用场景:
- 数据量较小的情况
- 数组基本有序的情况
- 需要稳定排序的场景
- 在线排序(可以边接收数据边排序)
-
优化技巧:可以使用二分查找优化查找插入位置的过程,但整体时间复杂度仍为 O(n²)
目录