手写冒泡排序
实现经典的冒泡排序算法,理解其原理和优化方法
问题
冒泡排序是一种基础的排序算法,通过重复遍历待排序数组,比较相邻元素并交换位置,使较大(或较小)的元素逐渐”冒泡”到数组末尾。请实现一个冒泡排序函数,要求能够正确排序数字数组,并考虑性能优化。
解答
/**
* 冒泡排序 - 基础版本
* @param {number[]} arr - 待排序数组
* @return {number[]} 排序后的数组
*/
function bubbleSort(arr) {
// 创建副本,避免修改原数组
const result = [...arr];
const len = result.length;
// 外层循环控制排序轮数
for (let i = 0; i < len - 1; i++) {
// 内层循环进行相邻元素比较和交换
for (let j = 0; j < len - 1 - i; j++) {
// 如果前一个元素大于后一个元素,则交换位置
if (result[j] > result[j + 1]) {
[result[j], result[j + 1]] = [result[j + 1], result[j]];
}
}
}
return result;
}
/**
* 冒泡排序 - 优化版本
* @param {number[]} arr - 待排序数组
* @return {number[]} 排序后的数组
*/
function bubbleSortOptimized(arr) {
const result = [...arr];
const len = result.length;
for (let i = 0; i < len - 1; i++) {
// 标记本轮是否发生交换
let swapped = false;
for (let j = 0; j < len - 1 - i; j++) {
if (result[j] > result[j + 1]) {
[result[j], result[j + 1]] = [result[j + 1], result[j]];
swapped = true;
}
}
// 如果本轮没有发生交换,说明数组已经有序,提前结束
if (!swapped) {
break;
}
}
return result;
}
使用示例
// 示例1:基本使用
const arr1 = [64, 34, 25, 12, 22, 11, 90];
console.log(bubbleSort(arr1));
// 输出: [11, 12, 22, 25, 34, 64, 90]
// 示例2:已排序数组(优化版本性能更好)
const arr2 = [1, 2, 3, 4, 5];
console.log(bubbleSortOptimized(arr2));
// 输出: [1, 2, 3, 4, 5]
// 优化版本只需一轮遍历即可结束
// 示例3:包含负数
const arr3 = [3, -1, 4, -5, 2];
console.log(bubbleSort(arr3));
// 输出: [-5, -1, 2, 3, 4]
// 示例4:包含重复元素
const arr4 = [5, 2, 8, 2, 9, 1];
console.log(bubbleSort(arr4));
// 输出: [1, 2, 2, 5, 8, 9]
// 示例5:空数组和单元素数组
console.log(bubbleSort([])); // 输出: []
console.log(bubbleSort([42])); // 输出: [42]
关键点
-
双层循环结构:外层循环控制排序轮数(n-1轮),内层循环进行相邻元素的比较和交换
-
比较范围优化:每轮排序后,最大元素会”冒泡”到末尾,因此内层循环范围可以减少
i(len - 1 - i) -
提前终止优化:使用
swapped标志位,如果某轮遍历没有发生任何交换,说明数组已经有序,可以提前结束排序 -
解构赋值交换:使用 ES6 的解构赋值
[a, b] = [b, a]简洁地交换两个元素,无需临时变量 -
不修改原数组:通过扩展运算符创建数组副本,保持函数的纯净性
-
时间复杂度:最坏和平均情况为 O(n²),最好情况(已排序)优化后为 O(n)
-
空间复杂度:O(1),只需要常数级别的额外空间(不考虑结果数组)
-
稳定性:冒泡排序是稳定排序算法,相等元素的相对位置不会改变
目录