|
1 | 1 | /** |
2 | | - * Copyright https://github.com/microwind All rights reserved. |
| 2 | + * Copyright © https://github.com/microwind All rights reserved. |
3 | 3 | * @author: jarryli@gmail.com |
4 | 4 | * @version: 1.0 |
5 | 5 | */ |
| 6 | +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { |
| 7 | + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { |
| 8 | + if (ar || !(i in from)) { |
| 9 | + if (!ar) ar = Array.prototype.slice.call(from, 0, i); |
| 10 | + ar[i] = from[i]; |
| 11 | + } |
| 12 | + } |
| 13 | + return to.concat(ar || Array.prototype.slice.call(from)); |
| 14 | +}; |
| 15 | +/** |
| 16 | + * 冒泡排序算法实现 |
| 17 | + * 提供四种不同的实现方式,适合不同场景和性能需求 |
| 18 | + */ |
6 | 19 | var BubbleSort = /** @class */ (function () { |
7 | | - function BubbleSort(arr) { |
8 | | - this.bubbleSort1(arr.slice(0)); |
9 | | - this.bubbleSort2(arr.slice(0)); |
10 | | - this.bubbleSort3(arr.slice(0)); |
| 20 | + function BubbleSort() { |
11 | 21 | } |
12 | 22 | /** |
13 | | - * 冒泡排序升序,将最大的元素冒泡到最后 |
14 | | - * 时间复杂度:O(n²),空间复杂度:O(1),稳定性:稳定 |
| 23 | + * 冒泡排序基础版本 - 升序排列 |
| 24 | + * |
| 25 | + * 算法原理: |
| 26 | + * 1. 从数组左端开始,依次比较相邻元素 |
| 27 | + * 2. 若左元素大于右元素,则交换位置 |
| 28 | + * 3. 继续向后比较,直到数组末尾 |
| 29 | + * 4. 重复以上过程,每轮都会将当前未排序部分的最大值"冒泡"到最后 |
| 30 | + * |
| 31 | + * 生活类比:就像水中的气泡,轻的气泡会自然上浮到水面 |
| 32 | + * 在排序中,大的数字会逐渐"上浮"到数组的右侧 |
| 33 | + * |
| 34 | + * 时间复杂度:O(n²) - 需要比较 n*(n-1)/2 次 |
| 35 | + * 空间复杂度:O(1) - 只使用常数个额外变量 |
| 36 | + * 稳定性:稳定 - 相等元素的相对位置不会改变 |
| 37 | + * |
| 38 | + * @param arr 待排序的数字数组 |
15 | 39 | */ |
16 | 40 | BubbleSort.prototype.bubbleSort1 = function (arr) { |
17 | 41 | var _a; |
18 | 42 | console.log('bubbleSort1 from left to right:'); |
19 | 43 | var len = arr.length; |
| 44 | + // 外循环:控制排序轮数,每轮确定一个最大值的位置 |
20 | 45 | for (var i = 0; i < len; i++) { |
| 46 | + // 内循环:控制比较次数,len-i-1 避免重复比较已排序部分 |
21 | 47 | for (var j = 0; j < len - i - 1; j++) { |
22 | | - // 自左往右每两个进行比较,把大的交换到右侧 |
23 | | - // 逐轮冒出最大数,已经排好序的不要再比较 |
| 48 | + // 关键点:自左往右每两个进行比较,把大的交换到右侧 |
24 | 49 | if (arr[j] > arr[j + 1]) { |
25 | | - ; |
| 50 | + // TS特点:解构赋值交换 |
26 | 51 | _a = [arr[j + 1], arr[j]], arr[j] = _a[0], arr[j + 1] = _a[1]; |
27 | 52 | } |
28 | | - // console.log('i=' + i, 'j=' + j, arr) |
29 | 53 | } |
30 | 54 | } |
31 | | - console.log(arr); |
| 55 | + this.printArray(arr, '基础升序版本排序结果'); |
32 | 56 | }; |
33 | 57 | /** |
34 | | - * 冒泡排序降序,将最小的元素冒泡到最后 |
35 | | - * 从右向左比较,逐轮冒出最小数 |
36 | | - * 时间复杂度:O(n²),空间复杂度:O(1),稳定性:稳定 |
| 58 | + * 冒泡排序基础版本 - 降序排列 |
| 59 | + * |
| 60 | + * 算法思路: |
| 61 | + * 与升序版本相反,从数组末尾开始比较 |
| 62 | + * 每轮将当前未排序部分的最小值"下沉"到左侧 |
| 63 | + * |
| 64 | + * 时间复杂度:O(n²) - 需要比较 n*(n-1)/2 次 |
| 65 | + * 空间复杂度:O(1) - 只使用常数个额外变量 |
| 66 | + * 稳定性:稳定 - 相等元素的相对位置不会改变 |
| 67 | + * |
| 68 | + * @param arr 待排序的数字数组 |
37 | 69 | */ |
38 | 70 | BubbleSort.prototype.bubbleSort2 = function (arr) { |
39 | 71 | var _a; |
40 | 72 | console.log('bubbleSort2 from right to left:'); |
41 | 73 | var len = arr.length; |
| 74 | + // 外循环:控制排序轮数,每轮确定一个最小值的位置 |
42 | 75 | for (var i = 0; i < len; i++) { |
| 76 | + // 内循环:从右向左比较,j > i 避免重复比较已排序部分 |
43 | 77 | for (var j = len - 1; j > i; j--) { |
44 | | - // 自右往左每两个进行比较,把小的交换到右侧 |
45 | | - // 逐轮冒出最小数,已经排好序的不要再比较 |
| 78 | + // 关键点:自右往左每两个进行比较,把小的交换到右侧 |
46 | 79 | if (arr[j - 1] < arr[j]) { |
47 | | - ; |
| 80 | + // TS特点:解构赋值交换 |
48 | 81 | _a = [arr[j], arr[j - 1]], arr[j - 1] = _a[0], arr[j] = _a[1]; |
49 | 82 | } |
50 | | - // console.log('i=' + i, 'j=' + j, arr) |
51 | 83 | } |
52 | 84 | } |
53 | | - console.log(arr); |
| 85 | + this.printArray(arr, '基础降序版本排序结果'); |
54 | 86 | }; |
55 | 87 | /** |
56 | | - * 冒泡排序升序,增加交换标志优化 |
57 | | - * 当某一轮无交换时提前终止,针对有序情况优化 |
| 88 | + * 冒泡排序优化版本 |
| 89 | + * |
| 90 | + * 优化思路: |
| 91 | + * 增加一个标志位,记录某一轮是否发生了元素交换 |
| 92 | + * 如果某一轮没有发生任何交换,说明数组已经完全有序 |
| 93 | + * 此时可以提前终止排序过程,避免不必要的比较 |
| 94 | + * |
| 95 | + * 优化效果: |
| 96 | + * - 对于完全有序的数组:时间复杂度从 O(n²) 优化到 O(n) |
| 97 | + * - 对于部分有序的数组:也会有明显的性能提升 |
| 98 | + * - 对于完全逆序的数组:性能与基础版本相同 |
| 99 | + * |
58 | 100 | * 时间复杂度:最好O(n),最坏O(n²),空间复杂度:O(1) |
| 101 | + * 稳定性:稳定 - 相等元素的相对位置不会改变 |
| 102 | + * |
| 103 | + * @param arr 待排序的数字数组 |
59 | 104 | */ |
60 | 105 | BubbleSort.prototype.bubbleSort3 = function (arr) { |
61 | 106 | var _a; |
62 | 107 | console.log('bubbleSort3 add flag:'); |
63 | | - // 增加一个标志,如果某一轮没有进行过任何的交换 |
64 | | - // 则说明当前数组已排好序,则不必继续后面的遍历, |
| 108 | + // 优化点:增加一个标志,如果某一轮没有进行过任何的交换 |
| 109 | + // 则说明当前数组已排好序,则不必继续后面的遍历 |
65 | 110 | var len = arr.length; |
66 | 111 | var flag = true; |
| 112 | + // 外循环:增加 flag 条件,当数组已有序时提前终止 |
67 | 113 | for (var i = 0; i < len && flag === true; i++) { |
68 | | - flag = false; |
69 | | - // console.warn('no. ' + i) |
| 114 | + flag = false; // 每轮开始时重置标志 |
| 115 | + // 内循环:控制比较次数,len-i-1 避免重复比较已排序部分 |
70 | 116 | for (var j = 0; j < len - i - 1; j++) { |
71 | | - // 自左往右每两个进行比较,把大的交换到右侧 |
72 | | - // 逐轮冒出最大数,已经排好序的不要再比较 |
| 117 | + // 关键点:自左往右每两个进行比较,把大的交换到右侧 |
73 | 118 | if (arr[j] > arr[j + 1]) { |
74 | | - flag = true; |
| 119 | + flag = true; // 发生交换,设置标志 |
| 120 | + // TS特点:解构赋值交换 |
75 | 121 | _a = [arr[j + 1], arr[j]], arr[j] = _a[0], arr[j + 1] = _a[1]; |
76 | 122 | } |
77 | | - // console.log('i=' + i, 'j=' + j, arr) |
78 | 123 | } |
79 | 124 | } |
80 | | - console.log(arr); |
| 125 | + this.printArray(arr, '优化版本排序结果'); |
| 126 | + }; |
| 127 | + /** |
| 128 | + * 插入冒泡排序法,分为左右两个序列,左侧为已排序,将待排项与左侧逐个对比并交换位置 |
| 129 | + * |
| 130 | + * 算法思路: |
| 131 | + * 将数组分为两部分:左侧已排序区域,右侧待排序区域 |
| 132 | + * 每次从待排序区域取出第一个元素,插入到已排序区域的正确位置 |
| 133 | + * |
| 134 | + * 这种方法结合了插入排序的思想,在某些情况下性能更好 |
| 135 | + * |
| 136 | + * 时间复杂度:O(n²),空间复杂度:O(1) |
| 137 | + * 稳定性:稳定 - 相等元素的相对位置不会改变 |
| 138 | + * |
| 139 | + * @param arr 待排序的数字数组 |
| 140 | + */ |
| 141 | + BubbleSort.prototype.bubbleSort4 = function (arr) { |
| 142 | + var _a; |
| 143 | + console.log('bubbleSort4:'); |
| 144 | + var len = arr.length; |
| 145 | + // 外循环:控制排序轮数,i 从 1 开始,因为第 0 个元素默认为已排序 |
| 146 | + for (var i = 1; i < len; i++) { |
| 147 | + // 内循环:在已排序区域中查找插入位置 |
| 148 | + for (var j = 0; j < i; j++) { |
| 149 | + // 关键点:如果待插入元素小于已排序区域的某个元素,则交换 |
| 150 | + if (arr[j] > arr[i]) { |
| 151 | + // TS特点:解构赋值交换 |
| 152 | + _a = [arr[j], arr[i]], arr[i] = _a[0], arr[j] = _a[1]; |
| 153 | + } |
| 154 | + } |
| 155 | + } |
| 156 | + this.printArray(arr, '插入式版本排序结果'); |
| 157 | + }; |
| 158 | + /** |
| 159 | + * 打印数组内容的辅助函数 |
| 160 | + * @param arr 要打印的数组 |
| 161 | + * @param label 数组的标签说明 |
| 162 | + */ |
| 163 | + BubbleSort.prototype.printArray = function (arr, label) { |
| 164 | + console.log("".concat(label, ": [").concat(arr.join(', '), "]")); |
| 165 | + }; |
| 166 | + /** |
| 167 | + * 性能测试辅助函数 |
| 168 | + * @param sortFunc 排序函数 |
| 169 | + * @param arr 测试数组 |
| 170 | + * @param name 测试名称 |
| 171 | + */ |
| 172 | + BubbleSort.prototype.performanceTest = function (sortFunc, arr, name) { |
| 173 | + // 创建数组副本,避免修改原数组 |
| 174 | + var testArr = __spreadArray([], arr, true); |
| 175 | + this.printArray(testArr, "".concat(name, "\u539F\u59CB\u6570\u7EC4")); |
| 176 | + // 开始计时 |
| 177 | + console.time(name); |
| 178 | + sortFunc(testArr); |
| 179 | + console.timeEnd(name); |
| 180 | + this.printArray(testArr, "".concat(name, "\u6392\u5E8F\u7ED3\u679C")); |
| 181 | + console.log(''); // 空行分隔 |
| 182 | + }; |
| 183 | + /** |
| 184 | + * 主程序:算法演示和性能测试 |
| 185 | + */ |
| 186 | + BubbleSort.prototype.run = function () { |
| 187 | + // 测试数据:包含重复元素和无序情况的典型数组 |
| 188 | + var testData = [7, 11, 9, 10, 12, 13, 8]; |
| 189 | + console.log('=== 冒泡排序算法演示 ===\n'); |
| 190 | + // 测试1:基础升序版本 |
| 191 | + this.performanceTest(this.bubbleSort1.bind(this), testData, '基础升序版本'); |
| 192 | + // 测试2:基础降序版本 |
| 193 | + this.performanceTest(this.bubbleSort2.bind(this), testData, '基础降序版本'); |
| 194 | + // 测试3:优化版本 |
| 195 | + this.performanceTest(this.bubbleSort3.bind(this), testData, '优化版本'); |
| 196 | + // 测试4:插入式版本 |
| 197 | + this.performanceTest(this.bubbleSort4.bind(this), testData, '插入式版本'); |
| 198 | + console.log('=== 算法对比总结 ==='); |
| 199 | + console.log('1. 基础版本:简单易懂,适合学习算法原理'); |
| 200 | + console.log('2. 降序版本:展示算法的灵活性,可按需排序'); |
| 201 | + console.log('3. 优化版本:通过标志位优化,适合实际应用'); |
| 202 | + console.log('4. 插入式版本:结合其他排序思想,性能更稳定'); |
| 203 | + console.log('\n建议:在实际项目中使用优化版本,在教学中使用基础版本'); |
81 | 204 | }; |
82 | 205 | return BubbleSort; |
83 | 206 | }()); |
84 | | -; |
85 | | -(function () { |
86 | | - // test |
87 | | - var arr = [7, 11, 9, 10, 12, 13, 8]; |
88 | | - console.time('bubbleSort'); |
89 | | - new BubbleSort(arr); |
90 | | - console.timeEnd('bubbleSort'); |
91 | | -})(); |
92 | | -/* |
93 | | -jarrys-MacBook-Pro:bubblesort jarry$ tsc BubbleSort.ts -t es2020 |
94 | | -jarrys-MacBook-Pro:bubblesort jarry$ node BubbleSort.js |
| 207 | +// 执行测试 |
| 208 | +var sorter = new BubbleSort(); |
| 209 | +sorter.run(); |
| 210 | +/* 打印结果 |
| 211 | +jarry@Mac bubblesort % ts-node BubbleSort.ts |
| 212 | +=== 冒泡排序算法演示 === |
| 213 | +
|
| 214 | +基础升序版本原始数组: [7, 11, 9, 10, 12, 13, 8] |
95 | 215 | bubbleSort1 from left to right: |
96 | | -[ |
97 | | - 7, 8, 9, 10, |
98 | | - 11, 12, 13 |
99 | | -] |
| 216 | +基础升序版本排序结果: [7, 8, 9, 10, 11, 12, 13] |
| 217 | +基础升序版本: 0.055ms |
| 218 | +基础升序版本排序结果: [7, 8, 9, 10, 11, 12, 13] |
| 219 | +
|
| 220 | +基础降序版本原始数组: [7, 11, 9, 10, 12, 13, 8] |
100 | 221 | bubbleSort2 from right to left: |
101 | | -[ |
102 | | - 13, 12, 11, 10, |
103 | | - 9, 8, 7 |
104 | | -] |
| 222 | +基础降序版本排序结果: [13, 12, 11, 10, 9, 8, 7] |
| 223 | +基础降序版本: 0.064ms |
| 224 | +基础降序版本排序结果: [13, 12, 11, 10, 9, 8, 7] |
| 225 | +
|
| 226 | +优化版本原始数组: [7, 11, 9, 10, 12, 13, 8] |
105 | 227 | bubbleSort3 add flag: |
106 | | -[ |
107 | | - 7, 8, 9, 10, |
108 | | - 11, 12, 13 |
109 | | -] |
110 | | -bubbleSort: 8.312ms |
111 | | -*/ |
| 228 | +优化版本排序结果: [7, 8, 9, 10, 11, 12, 13] |
| 229 | +优化版本: 0.03ms |
| 230 | +优化版本排序结果: [7, 8, 9, 10, 11, 12, 13] |
| 231 | +
|
| 232 | +插入式版本原始数组: [7, 11, 9, 10, 12, 13, 8] |
| 233 | +bubbleSort4: |
| 234 | +插入式版本排序结果: [7, 8, 9, 10, 11, 12, 13] |
| 235 | +插入式版本: 0.025ms |
| 236 | +插入式版本排序结果: [7, 8, 9, 10, 11, 12, 13] |
| 237 | +
|
| 238 | +=== 算法对比总结 === |
| 239 | +1. 基础版本:简单易懂,适合学习算法原理 |
| 240 | +2. 降序版本:展示算法的灵活性,可按需排序 |
| 241 | +3. 优化版本:通过标志位优化,适合实际应用 |
| 242 | +4. 插入式版本:结合其他排序思想,性能更稳定 |
| 243 | +
|
| 244 | +建议:在实际项目中使用优化版本,在教学中使用基础版本 |
| 245 | +*/ |
0 commit comments