Sắp xếp nổi bọt
Cập nhật lần cuối
Sắp xếp nổi bọt (bubble sort) liên tục duyệt qua danh sách, so sánh từng cặp phần tử kề nhau và đổi chỗ chúng nếu sai thứ tự. Sau mỗi lượt đầy đủ, giá trị lớn nhất còn lại đã "nổi" lên đúng vị trí của nó ở cuối, nên mỗi lượt sau xét ít hơn một phần tử. Nhấn Phát ở trên để xem các lần so sánh và đổi chỗ, hoặc chạy từng bước một.
Đây là một trong những thuật toán sắp xếp dễ hiểu nhất, nên rất phù hợp để làm thuật toán đầu tiên, nhưng thời gian chạy O(n²) khiến nó không thực tế với dữ liệu lớn.
Độ phức tạp thời gian và không gian
| Trường hợp | Độ phức tạp | Ghi chú |
|---|---|---|
| Tốt nhất | O(n) | Mảng đã sắp xếp, có kiểm tra dừng sớm |
| Trung bình | O(n²) | Thứ tự ngẫu nhiên |
| Xấu nhất | O(n²) | Sắp xếp ngược |
| Không gian | O(1) | Sắp xếp tại chỗ, chỉ cần một biến tạm |
| Ổn định | Có | Các phần tử bằng nhau giữ nguyên thứ tự tương đối |
Từng bước thực hiện
| Bước | Điều gì xảy ra |
|---|---|
| 1 | Bắt đầu từ đầu mảng. |
| 2 | So sánh phần tử hiện tại với phần tử kế tiếp. |
| 3 | Nếu chúng sai thứ tự, đổi chỗ hai phần tử. |
| 4 | Dịch sang phải một vị trí và lặp lại đến cuối mảng (một lượt). |
| 5 | Lặp lại các lượt; mỗi lượt cố định thêm một phần tử ở cuối. |
| 6 | Dừng khi một lượt đầy đủ không có lần đổi chỗ nào. |
Ví dụ minh họa
Sắp xếp [5, 2, 4, 1]:
| Lượt | Mảng | Thao tác |
|---|---|---|
| 1 | [2, 4, 1, 5] | Đổi chỗ 5,2, rồi 5,4, rồi 5,1; 5 nổi lên cuối mảng. |
| 2 | [2, 1, 4, 5] | 2,4 đúng thứ tự; đổi chỗ 4,1; 4,5 đúng thứ tự; 4 đã vào đúng vị trí. |
| 3 | [1, 2, 4, 5] | Đổi chỗ 2,1; phần còn lại đã đúng thứ tự; 2 đã vào đúng vị trí. |
| 4 | [1, 2, 4, 5] | Một lượt đầy đủ không có lần đổi chỗ nào, nên mảng đã được sắp xếp và thuật toán dừng. |
Khi nào nên dùng sắp xếp nổi bọt
| Nên dùng khi | Tránh dùng khi |
|---|---|
| Dạy hoặc học cách các thuật toán sắp xếp bằng so sánh hoạt động | Sắp xếp dữ liệu lớn, khi O(n²) là quá chậm |
Dữ liệu rất nhỏ hoặc gần như đã sắp xếp (với dừng sớm, thuật toán tiệm cận O(n)) | Bạn cần thuật toán sắp xếp đa dụng nhanh nhất: hãy dùng quicksort hoặc merge sort |
| Bạn cần một thuật toán sắp xếp ổn định, tại chỗ với rất ít code | Dữ liệu theo thứ tự ngẫu nhiên và hiệu năng là yếu tố quan trọng |
| Kiểm tra xem một danh sách ngắn đã được sắp xếp hay chưa chỉ trong một lượt | Việc ghi nhiều lần tốn kém (ví dụ bộ nhớ flash); sắp xếp chọn đổi chỗ ít hơn |
Code Bubble Sort
Một cách cài đặt Bubble Sort gọn gàng, chạy được ngay bằng Python, JavaScript, Java, C++, C, Pseudocode. Chọn một ngôn ngữ, sao chép code hoặc mở sẵn trong Coddy Playground.
Code Bubble Sort bằng 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)Code Bubble Sort bằng 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]));Code Bubble Sort bằng 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}Code Bubble Sort bằng 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}Code Bubble Sort bằng 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}Code Bubble Sort bằng 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 iCâu hỏi thường gặp về sắp xếp nổi bọt
Độ phức tạp thời gian của sắp xếp nổi bọt là bao nhiêu?
O(n²) ở trường hợp trung bình và xấu nhất do có hai vòng lặp lồng nhau. Với tối ưu dừng sớm, thuật toán có thể đạt O(n) trên mảng đã sắp xếp. Nó dùng thêm O(1) bộ nhớ.Sắp xếp nổi bọt có ổn định không?
Có. Sắp xếp nổi bọt chỉ đổi chỗ hai phần tử kề nhau khi chúng thực sự sai thứ tự, nên các phần tử bằng nhau không bao giờ vượt qua nhau và giữ nguyên thứ tự tương đối ban đầu.
Vì sao gọi là sắp xếp nổi bọt?
Ở mỗi lượt, giá trị lớn nhất chưa được sắp xếp di chuyển từng bước về cuối mảng, giống như bọt khí nổi lên mặt nước, vì thế mới có tên "nổi bọt".
Sắp xếp nổi bọt và sắp xếp chèn khác nhau như thế nào?
O(n²), ổn định và sắp xếp tại chỗ, nhưng di chuyển dữ liệu khác nhau: sắp xếp nổi bọt liên tục đổi chỗ các cặp kề nhau sai thứ tự, còn sắp xếp chèn lấy từng phần tử và dời nó về đúng vị trí trong phần đầu đã sắp xếp. Sắp xếp chèn thường ghi ít hơn và chạy nhanh hơn trong thực tế, nhất là với dữ liệu gần như đã sắp xếp.Khi nào nên dùng sắp xếp nổi bọt thay cho quicksort?
O(n log n) của quicksort vượt xa O(n²) của sắp xếp nổi bọt, trừ khi dữ liệu rất nhỏ. Sắp xếp nổi bọt chỉ đáng dùng khi danh sách rất nhỏ hoặc gần như đã sắp xếp, hoặc khi bạn muốn một thuật toán sắp xếp ổn định đơn giản nhất để giảng dạy.Tối ưu dừng sớm có thay đổi trường hợp xấu nhất của sắp xếp nổi bọt không?
O(n) với dữ liệu đã sắp xếp, nhưng một mảng sắp xếp ngược vẫn cần mọi lần so sánh, nên trường hợp xấu nhất vẫn là O(n²). Tối ưu này chỉ giúp trường hợp tốt nhất và dữ liệu gần như đã sắp xếp.