|
2 | 2 |
|
3 | 3 | > **冒泡排序(Bubble Sort)基本思想**:
|
4 | 4 | >
|
5 |
| -> 经过多次迭代,通过相邻元素之间的比较与交换,使值较小的元素逐步从后面移到前面,值较大的元素从前面移到后面。 |
| 5 | +> 通过相邻元素的比较与交换,将较大的元素逐步「冒泡」到数组末尾,较小的元素自然「下沉」到数组开头。 |
6 | 6 |
|
7 |
| -这个过程就像水底的气泡一样从底部向上「冒泡」到水面,这也是冒泡排序法名字的由来。 |
| 7 | +冒泡排序的名字来源于这个过程:就像水中的气泡从底部向上浮到水面一样,较大的元素会逐步移动到数组的末尾。 |
8 | 8 |
|
9 |
| -接下来,我们使用「冒泡」的方式来模拟一下这个过程。 |
10 |
| - |
11 |
| -1. 首先将数组想象是一排「泡泡」,元素值的大小与泡泡的大小成正比。 |
12 |
| -2. 然后从左到右依次比较相邻的两个「泡泡」: |
13 |
| - 1. 如果左侧泡泡大于右侧泡泡,则交换两个泡泡的位置。 |
14 |
| - 2. 如果左侧泡泡小于等于右侧泡泡,则两个泡泡保持不变。 |
15 |
| -3. 这 $1$ 趟遍历完成之后,最大的泡泡就会放置到所有泡泡的最右侧,就像是「泡泡」从水底向上浮到了水面。 |
| 9 | +**我们使用「冒泡」的方式来模拟一下这个过程**: |
| 10 | +1. 将数组元素想象成大小不同的「泡泡」,值越大的元素「泡泡」越大。 |
| 11 | +2. 从左到右依次比较相邻的两个元素。 |
| 12 | +3. 如果左侧元素大于右侧元素,则交换位置。 |
| 13 | +4. 每完成一趟遍历,最大的元素就会「浮」到最右侧。 |
16 | 14 |
|
17 | 15 | ::: tabs#bubble
|
18 | 16 |
|
|
48 | 46 |
|
49 | 47 | ## 2. 冒泡排序算法步骤
|
50 | 48 |
|
51 |
| -假设数组的元素个数为 $n$ 个,则冒泡排序的算法步骤如下: |
| 49 | +对于长度为 $n$ 的数组,冒泡排序的步骤如下: |
52 | 50 |
|
53 |
| -1. 第 $1$ 趟「冒泡」:对前 $n$ 个元素执行「冒泡」,从而使第 $1$ 个值最大的元素放置在正确位置上。 |
54 |
| - 1. 先将序列中第 $1$ 个元素与第 $2$ 个元素进行比较,如果前者大于后者,则两者交换位置,否则不交换。 |
55 |
| - 2. 然后将第 $2$ 个元素与第 $3$ 个元素比较,如果前者大于后者,则两者交换位置,否则不交换。 |
56 |
| - 3. 依次类推,直到第 $n - 1$ 个元素与第 $n$ 个元素比较(或交换)为止。 |
57 |
| - 4. 经过第 $1$ 趟排序,使得 $n$ 个元素中第 $i$ 个值最大元素被安置在第 $n$ 个位置上。 |
58 |
| -2. 第 $2$ 趟「冒泡」:对前 $n - 1$ 个元素执行「冒泡」,从而使第 $2$ 个值最大的元素放置在正确位置上。 |
59 |
| - 1. 先将序列中第 $1$ 个元素与第 $2$ 个元素进行比较,若前者大于后者,则两者交换位置,否则不交换。 |
60 |
| - 2. 然后将第 $2$ 个元素与第 $3$ 个元素比较,若前者大于后者,则两者交换位置,否则不交换。 |
61 |
| - 3. 依次类推,直到对 $n - 2$ 个元素与第 $n - 1$ 个元素比较(或交换)为止。 |
62 |
| - 4. 经过第 $2$ 趟排序,使得数组中第 $2$ 个值最大元素被安置在第 $n$ 个位置上。 |
63 |
| -3. 依次类推,重复上述「冒泡」过程,直到某一趟排序过程中不出现元素交换位置的动作,则排序结束。 |
| 51 | +1. 第 $1$ 趟冒泡:对前 $n$ 个元素依次比较相邻元素,将较大的元素向右交换,最终使最大值移动到数组末尾(第 $n$ 个位置)。 |
| 52 | + 1. 比较第 $1$ 个和第 $2$ 个元素,若前者大于后者则交换。 |
| 53 | + 2. 比较第 $2$ 个和第 $3$ 个元素,若前者大于后者则交换。 |
| 54 | + 3. 以此类推,直到比较第 $n - 1$ 个和第 $n$ 个元素。 |
| 55 | + 4. 完成后,最大元素已位于末尾。 |
| 56 | +2. 第 $2$ 趟冒泡:对前 $n-1$ 个元素重复上述过程,将次大值移动到倒数第二个位置(第 $n-1$ 个位置)。 |
| 57 | + 1. 比较第 $1$ 个和第 $2$ 个元素,若前者大于后者则交换。 |
| 58 | + 2. 比较第 $2$ 个和第 $3$ 个元素,若前者大于后者则交换。 |
| 59 | + 3. 以此类推,直到比较第 $n-2$ 个和第 $n-1$ 个元素。 |
| 60 | + 4. 完成后,次大元素已位于倒数第二位。 |
| 61 | +3. 持续进行上述冒泡过程,每一趟比较的元素个数递减,直到某一趟未发生任何交换,说明数组已完全有序,排序结束。 |
64 | 62 |
|
65 |
| -我们以 $[5, 2, 3, 6, 1, 4]$ 为例,演示一下冒泡排序算法的整个步骤。 |
| 63 | +以数组 $[5, 2, 3, 6, 1, 4]$ 为例,演示一下冒泡排序的算法步骤。 |
66 | 64 |
|
67 |
| - |
| 65 | + |
68 | 66 |
|
69 | 67 | ## 3. 冒泡排序代码实现
|
70 | 68 |
|
71 | 69 | ```python
|
72 | 70 | class Solution:
|
73 | 71 | def bubbleSort(self, nums: [int]) -> [int]:
|
74 |
| - # 第 i 趟「冒泡」 |
75 |
| - for i in range(len(nums) - 1): |
76 |
| - flag = False # 是否发生交换的标志位 |
77 |
| - # 从数组中前 n - i + 1 个元素的第 1 个元素开始,相邻两个元素进行比较 |
78 |
| - for j in range(len(nums) - i - 1): |
79 |
| - # 相邻两个元素进行比较,如果前者大于后者,则交换位置 |
| 72 | + """冒泡排序算法实现""" |
| 73 | + n = len(nums) |
| 74 | + # 外层循环控制趟数,每一趟将当前未排序区间的最大值“冒泡”到末尾 |
| 75 | + for i in range(n - 1): |
| 76 | + swapped = False # 记录本趟是否发生过交换 |
| 77 | + # 内层循环负责相邻元素两两比较,将较大值后移 |
| 78 | + for j in range(n - i - 1): |
| 79 | + # 如果前一个元素大于后一个元素,则交换 |
80 | 80 | if nums[j] > nums[j + 1]:
|
81 | 81 | nums[j], nums[j + 1] = nums[j + 1], nums[j]
|
82 |
| - flag = True |
83 |
| - if not flag: # 此趟遍历未交换任何元素,直接跳出 |
| 82 | + swapped = True # 发生了交换 |
| 83 | + # 如果本趟没有发生任何交换,说明数组已经有序,可以提前结束 |
| 84 | + if not swapped: |
84 | 85 | break
|
85 |
| - |
86 |
| - return nums |
87 |
| - |
| 86 | + return nums # 返回排序后的数组 |
| 87 | + |
88 | 88 | def sortArray(self, nums: [int]) -> [int]:
|
| 89 | + """排序数组的接口,调用冒泡排序""" |
89 | 90 | return self.bubbleSort(nums)
|
90 | 91 | ```
|
91 | 92 |
|
92 | 93 | ## 4. 冒泡排序算法分析
|
93 | 94 |
|
94 |
| -- **最佳时间复杂度**:$O(n)$。最好的情况下(初始时序列已经是升序排列),只需经过 $1$ 趟排序,总共经过 $n$ 次元素之间的比较,并且不移动元素,算法就可以结束排序。因此,冒泡排序算法的最佳时间复杂度为 $O(n)$。 |
95 |
| -- **最坏时间复杂度**:$O(n^2)$。最差的情况下(初始时序列已经是降序排列,或者最小值元素处在序列的最后),则需要进行 $n$ 趟排序,总共进行 $∑^n_{i=2}(i−1) = \frac{n(n−1)}{2}$ 次元素之间的比较,因此,冒泡排序算法的最坏时间复杂度为 $O(n^2)$。 |
96 |
| -- **空间复杂度**:$O(1)$。冒泡排序为原地排序算法,只用到指针变量 $i$、$j$ 以及标志位 $flag$ 等常数项的变量。 |
97 |
| -- **冒泡排序适用情况**:冒泡排序方法在排序过程中需要移动较多次数的元素,并且排序时间效率比较低。因此,冒泡排序方法比较适合于参加排序序列的数据量较小的情况,尤其是当序列的初始状态为基本有序的情况。 |
98 |
| -- **排序稳定性**:由于元素交换是在相邻元素之间进行的,不会改变相等元素的相对顺序,因此,冒泡排序法是一种 **稳定排序算法**。 |
| 95 | +| 指标 | 复杂度 | 说明 | |
| 96 | +|------|--------|------| |
| 97 | +| **最佳时间复杂度** | $O(n)$ | 数组已有序,只需一趟遍历 | |
| 98 | +| **最坏时间复杂度** | $O(n^2)$ | 数组逆序,需要 $n$ 趟遍历 | |
| 99 | +| **平均时间复杂度** | $O(n^2)$ | 一般情况下的复杂度 | |
| 100 | +| **空间复杂度** | $O(1)$ | 原地排序,只使用常数空间 | |
| 101 | +| **稳定性** | ✅ 稳定 | 相等元素相对位置不变 | |
99 | 102 |
|
100 |
| -## 5. 总结 |
| 103 | +**适用场景**: |
| 104 | +- 数据量较小($n < 50$) |
| 105 | +- 数据基本有序 |
101 | 106 |
|
102 |
| -冒泡排序是一种简单的排序算法。它通过多次比较相邻元素并交换位置,将较大的元素逐步移动到数组末尾。 |
103 |
| - |
104 |
| -冒泡排序的时间复杂度取决于数据情况。最好情况下是 $O(n)$,最坏情况下是 $O(n^2)$。它只需要常数级别的额外空间,空间复杂度是 $O(1)$。 |
| 107 | +## 5. 总结 |
105 | 108 |
|
106 |
| -冒泡排序适合数据量小的场景。当数据基本有序时,它的效率较高。由于交换只在相邻元素间进行,冒泡排序是稳定的排序算法。 |
| 109 | +冒泡排序是最简单的排序算法之一,通过相邻元素比较交换实现排序。虽然实现简单,但效率较低。 |
107 | 110 |
|
108 |
| -冒泡排序容易实现,但效率较低。在实际应用中,通常选择更高效的排序算法处理大规模数据。 |
| 111 | +**优点**:实现简单,稳定排序,空间复杂度低 |
| 112 | +**缺点**:时间复杂度高,交换次数多 |
109 | 113 |
|
110 | 114 | ## 练习题目
|
111 | 115 |
|
|
0 commit comments