冒泡排序
最后更新
冒泡排序反复遍历列表,比较每一对相邻元素,如果顺序不对就交换它们。每完成一整轮,剩余元素中的最大值就会“冒泡”到末尾的正确位置,所以每一轮需要查看的元素都少一个。点击上方的“播放”观察比较和交换的过程,也可以一步一步地执行。
它是最容易理解的排序算法之一,非常适合作为入门的第一个算法,但它 O(n²) 的运行时间让它不适合处理大规模输入。
时间和空间复杂度
| 情况 | 复杂度 | 说明 |
|---|---|---|
| 最好情况 | O(n) | 已经有序,并带有提前退出的检查 |
| 平均情况 | O(n²) | 随机顺序 |
| 最坏情况 | O(n²) | 完全逆序 |
| 空间 | O(1) | 原地排序,只需一个临时变量 |
| 稳定性 | 稳定 | 相等的元素保持原有的相对顺序 |
分步说明
| 步骤 | 发生了什么 |
|---|---|
| 1 | 从数组开头开始。 |
| 2 | 把当前元素和下一个元素比较。 |
| 3 | 如果顺序不对,就交换它们。 |
| 4 | 向右移动一位,重复直到末尾(这是一轮)。 |
| 5 | 重复多轮,每一轮都在末尾再确定一个元素。 |
| 6 | 当一整轮都没有发生交换时停止。 |
示例演算
对 [5, 2, 4, 1] 排序:
| 轮次 | 数组 | 操作 |
|---|---|---|
| 1 | [2, 4, 1, 5] | 交换 5,2,再交换 5,4,再交换 5,1;5 冒泡到末尾。 |
| 2 | [2, 1, 4, 5] | 2,4 顺序正确;交换 4,1;4,5 顺序正确;4 已就位。 |
| 3 | [1, 2, 4, 5] | 交换 2,1;其余已经有序;2 已就位。 |
| 4 | [1, 2, 4, 5] | 一整轮没有发生交换,说明数组已经有序,算法结束。 |
什么时候使用冒泡排序
| 适合使用的情况 | 不适合使用的情况 |
|---|---|
| 教学或学习比较排序的原理 | 对大规模输入排序,O(n²) 太慢了 |
输入很小或几乎有序(加上提前退出后接近 O(n)) | 需要最快的通用排序,应使用快速排序或归并排序 |
| 需要一个稳定、原地、代码量极少的排序 | 数据顺序随机,并且对性能有要求 |
| 用一轮检查一个短列表是否已经有序 | 写入代价很高(例如闪存),选择排序的交换次数更少 |
Bubble Sort 代码
简洁、可运行的 Bubble Sort 实现,提供 Python, JavaScript, Java, C++, C, Pseudocode 版本。选择一种语言,复制代码,或在 Coddy 在线编辑器中直接打开。
Bubble Sort 的 Python 代码
Python
1def bubble_sort(a):2 n = len(a)3 for i in range(n - 1):4 swapped = False5 for j in range(n - 1 - i):6 if a[j] > a[j + 1]:7 a[j], a[j + 1] = a[j + 1], a[j]8 swapped = True9 if not swapped:10 break # no swaps means the list is already sorted11 return a12
13
14nums = [5, 1, 4, 2, 8]15print("Before:", nums)16bubble_sort(nums)17print("After: ", nums)Bubble Sort 的 JavaScript 代码
JavaScript
1function bubbleSort(a) {2 for (let end = a.length - 1; end > 0; end--) {3 let swapped = false;4 for (let j = 0; j < end; j++) {5 if (a[j] > a[j + 1]) {6 [a[j], a[j + 1]] = [a[j + 1], a[j]];7 swapped = true;8 }9 }10 if (!swapped) break; // Already sorted: stop early11 }12 return a;13}14
15const data = [5, 2, 9, 1, 7, 3];16console.log("Before:", data);17console.log("Sorted:", bubbleSort([...data]));Bubble Sort 的 Java 代码
Java
1import java.util.Arrays;2
3public class Main {4 static void bubbleSort(int[] arr) {5 for (int i = arr.length - 1; i > 0; i--) {6 boolean swapped = false;7 for (int j = 0; j < i; j++) {8 if (arr[j] > arr[j + 1]) {9 int tmp = arr[j];10 arr[j] = arr[j + 1];11 arr[j + 1] = tmp;12 swapped = true;13 }14 }15 if (!swapped) break; // already sorted16 }17 }18
19 public static void main(String[] args) {20 int[] arr = {5, 1, 4, 2, 8, 3};21 System.out.println("Before: " + Arrays.toString(arr));22 bubbleSort(arr);23 System.out.println("After: " + Arrays.toString(arr));24 }25}Bubble Sort 的 C++ 代码
C++
1#include <iostream>2#include <utility>3#include <vector>4
5void printVec(const std::vector<int>& a) {6 for (int x : a) std::cout << x << " ";7 std::cout << "\n";8}9
10void bubbleSort(std::vector<int>& a) {11 for (size_t pass = 0; pass + 1 < a.size(); ++pass) {12 bool swapped = false;13 // Each pass bubbles the largest remaining value to the end14 for (size_t j = 0; j + 1 < a.size() - pass; ++j) {15 if (a[j] > a[j + 1]) {16 std::swap(a[j], a[j + 1]);17 swapped = true;18 }19 }20 if (!swapped) break; // already sorted21 }22}23
24int main() {25 std::vector<int> data = {5, 1, 4, 2, 8, 3};26 std::cout << "Before: ";27 printVec(data);28 bubbleSort(data);29 std::cout << "After: ";30 printVec(data);31 return 0;32}Bubble Sort 的 C 代码
C
1#include <stdbool.h>2#include <stdio.h>3
4void printArr(const int a[], int n) {5 for (int i = 0; i < n; i++) printf("%d ", a[i]);6 printf("\n");7}8
9void bubbleSort(int a[], int n) {10 for (int pass = 0; pass < n - 1; pass++) {11 bool swapped = false;12 // Each pass bubbles the largest remaining value to the end13 for (int j = 0; j < n - 1 - pass; j++) {14 if (a[j] > a[j + 1]) {15 int tmp = a[j];16 a[j] = a[j + 1];17 a[j + 1] = tmp;18 swapped = true;19 }20 }21 if (!swapped) break; // already sorted22 }23}24
25int main(void) {26 int data[] = {5, 1, 4, 2, 8, 3};27 int n = sizeof(data) / sizeof(data[0]);28 printf("Before: ");29 printArr(data, n);30 bubbleSort(data, n);31 printf("After: ");32 printArr(data, n);33 return 0;34}Bubble Sort 的 Pseudocode 代码
Pseudocode
1DECLARE nums : ARRAY[1:7] OF INTEGER2DECLARE n : INTEGER3n ← 74nums[1] ← 75nums[2] ← 36nums[3] ← 97nums[4] ← 18nums[5] ← 59nums[6] ← 810nums[7] ← 211DECLARE i : INTEGER12DECLARE j : INTEGER13DECLARE temp : INTEGER14
15// Bubble the largest remaining value to the end on each pass16FOR i ← 1 TO n - 117 FOR j ← 1 TO n - i18 IF nums[j] > nums[j + 1] THEN19 temp ← nums[j]20 nums[j] ← nums[j + 1]21 nums[j + 1] ← temp22 ENDIF23 NEXT j24NEXT i25
26FOR i ← 1 TO n27 OUTPUT nums[i]28NEXT i冒泡排序常见问题
冒泡排序的时间复杂度是多少?
由于两层嵌套循环,冒泡排序在平均和最坏情况下的运行时间是
O(n²)。加上提前退出的优化后,在已经有序的数组上可以达到 O(n)。它只占用 O(1) 的额外空间。冒泡排序是稳定的吗?
是的。冒泡排序只在相邻元素严格逆序时才交换,所以相等的元素永远不会互相越过,会保持原有的相对顺序。
为什么叫冒泡排序?
每一轮中,未排序部分的最大值都会一步步移向数组末尾,就像气泡浮上水面一样,所以叫“冒泡”排序。
冒泡排序和插入排序有什么区别?
两者都是
O(n²)、稳定且原地的排序,但移动数据的方式不同:冒泡排序反复交换相邻的逆序对,而插入排序依次取出每个元素,把它向前移到已排序前缀中的正确位置。插入排序通常写入次数更少,实际运行更快,在几乎有序的数据上尤其明显。什么时候该用冒泡排序而不是快速排序?
在实际工作中几乎从不。除了极小的输入,快速排序
O(n log n) 的平均时间都远胜冒泡排序的 O(n²)。只有当列表非常小或几乎有序,或者教学时需要最简单的稳定排序时,冒泡排序才值得一用。提前退出的优化会改变冒泡排序的最坏情况吗?
不会。记录每一轮是否发生了交换,可以让冒泡排序提前停止,在已有序的输入上达到
O(n);但完全逆序的数组仍然需要做完所有比较,所以最坏情况仍是 O(n²)。这个优化只对最好情况和几乎有序的情况有帮助。