Skip to content

Sắp xếp O(n²) — Bubble, Selection, Insertion

Trong một buổi phỏng vấn tại một công ty lớn, ứng viên được hỏi: "Dữ liệu gần như đã sắp xếp sẵn — bạn chọn thuật toán O(n²) nào?". Ứng viên lúng túng. Người phỏng vấn không cần bạn nói về Quick Sort hay Merge Sort — họ muốn kiểm tra liệu bạn có thật sự hiểu trade-offs giữa ba thuật toán cơ bản nhất hay không.

Ba thuật toán này không chỉ là bài tập cho người mới. Chúng dạy bạn ba khái niệm nền tảng mà mọi kỹ sư phải nắm: stability (tính ổn định), in-place sorting (sắp xếp tại chỗ), và adaptive behavior (hành vi thích nghi). Hiểu sâu ba thuật toán O(n²) chính là bước đệm vững chắc để hiểu tại sao O(n log n) lại quan trọng.

🎯 Mục tiêu

🎯 Mục tiêu bài học

  • Hiểu cơ chế hoạt động của 3 thuật toán sắp xếp O(n²)
  • Phân biệt stable vs unstable, adaptive vs non-adaptive
  • Biết khi nào O(n²) thắng O(n log n) trong thực tế
  • Đọc được visualizer state transitions cho từng thuật toán
  • Tránh 5 sai lầm kinh điển trong phỏng vấn và code review

Bức tranh tư duy — 3 chiến thuật xếp bài

Hãy tưởng tượng bạn đang cầm một bộ bài lộn xộn và muốn xếp theo thứ tự tăng dần. Ba người chơi — mỗi người một chiến thuật:

Bộ bài ban đầu:  [8] [3] [5] [1] [9]

┌─────────────────────────────────────────────────────────────┐
│  🫧 Người chơi A — Bubble Sort                             │
│  "Mình so sánh hai lá kề nhau, đổi chỗ nếu sai thứ tự,   │
│   lặp lại cho đến khi hết cần đổi."                        │
│                                                             │
│  [8][3] → đổi! → [3][8][5][1][9]                          │
│  [8][5] → đổi! → [3][5][8][1][9]                          │
│  [8][1] → đổi! → [3][5][1][8][9]                          │
│  ... lá lớn nhất "nổi" lên cuối mỗi pass                  │
├─────────────────────────────────────────────────────────────┤
│  🔍 Người chơi B — Selection Sort                          │
│  "Mình tìm lá nhỏ nhất, đặt vào vị trí đầu tiên,         │
│   rồi tìm lá nhỏ nhất còn lại, đặt vào vị trí thứ hai."  │
│                                                             │
│  Tìm min = [1] → đổi với vị trí 0 → [1][3][5][8][9]      │
│  Tìm min = [3] → đã đúng chỗ      → [1][3][5][8][9]      │
│  ... mỗi pass chỉ có ĐÚNG 1 lần swap                      │
├─────────────────────────────────────────────────────────────┤
│  📥 Người chơi C — Insertion Sort                          │
│  "Mình cầm từng lá một, chèn vào đúng vị trí trong        │
│   phần đã sắp xếp — giống xếp bài trên tay."              │
│                                                             │
│  Tay: [8]         lấy [3] → chèn trước 8 → [3][8]        │
│  Tay: [3][8]      lấy [5] → chèn giữa    → [3][5][8]     │
│  Tay: [3][5][8]   lấy [1] → chèn đầu     → [1][3][5][8]  │
│  Tay: [1][3][5][8] lấy [9] → chèn cuối   → [1][3][5][8][9]│
└─────────────────────────────────────────────────────────────┘

Cùng kết quả — nhưng cách làm, số lần swap, và hiệu quả khác nhau hoàn toàn.

Bảng so sánh tổng quan

Tiêu chíBubble SortSelection SortInsertion Sort
Best caseO(n) ✅O(n²)O(n) ✅
Average caseO(n²)O(n²)O(n²)
Worst caseO(n²)O(n²)O(n²)
SpaceO(1)O(1)O(1)
Stable✅ Có❌ Không✅ Có
Adaptive✅ Có (với cờ)❌ Không✅ Có
Số lần swapO(n²)O(n) ✅O(n²)
Số lần so sánhO(n²)O(n²)O(n²)
Use case lý tưởngDạy thuật toánGhi ít (flash memory)Dữ liệu gần sorted

💡 HPN Pro Tip — Đọc bảng này

Khi phỏng vấn hỏi "so sánh 3 thuật toán O(n²)", đừng chỉ đọc từng dòng — hãy nêu điểm khác biệt nổi bật: Insertion Sort adaptive + stable, Selection Sort ít swap nhất, Bubble Sort chỉ tốt khi có swapped flag.

Cốt lõi kỹ thuật

🫧 Bubble Sort — Sắp xếp nổi bọt

Cơ chế: So sánh hai phần tử kề nhau, đổi chỗ nếu sai thứ tự. Lặp lại các pass cho đến khi mảng được sắp xếp. Phần tử lớn nhất "nổi bọt" lên cuối mảng sau mỗi pass.

Tối ưu then chốt: Cờ swapped — nếu một pass hoàn thành mà không có swap nào, mảng đã sắp xếp → dừng sớm → best case O(n).

Minh hoạ từng bước — [5, 1, 4, 2, 8]

Pass 1:  [5, 1, 4, 2, 8]
         [5,1] → swap → [1, 5, 4, 2, 8]
         [5,4] → swap → [1, 4, 5, 2, 8]
         [5,2] → swap → [1, 4, 2, 5, 8]
         [5,8] → ok   → [1, 4, 2, 5, 8]   ← 8 settled ✅

Pass 2:  [1, 4, 2, 5, 8]
         [1,4] → ok   → [1, 4, 2, 5, 8]
         [4,2] → swap → [1, 2, 4, 5, 8]
         [4,5] → ok   → [1, 2, 4, 5, 8]   ← 5 settled ✅

Pass 3:  [1, 2, 4, 5, 8]
         [1,2] → ok
         [2,4] → ok
         swapped = false → BREAK! 🎉        ← Early exit

Visualizer State Transitions — Bubble Sort

VISUALIZER STATES — Bubble Sort:
State 1: COMPARE      — highlight arr[j] và arr[j+1] màu vàng
State 2: SWAP         — hoán đổi, cả hai phần tử chuyển cam
State 3: SETTLED      — phần tử đã ở đúng vị trí cuối → xanh lá
State 4: PASS_COMPLETE — ranh giới biên dịch trái (n - i - 1)
State 5: DONE         — tất cả phần tử xanh lá

Triển khai chuẩn

cpp
void bubbleSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        bool swapped = false;
        for (int j = 0; j < n - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
                swapped = true;
            }
        }
        if (!swapped) break; // Already sorted
    }
}
java
public static void bubbleSort(int[] arr) {
    int n = arr.length;
    for (int i = 0; i < n - 1; i++) {
        boolean swapped = false;
        for (int j = 0; j < n - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = true;
            }
        }
        if (!swapped) break;
    }
}
python
def bubble_sort(arr: list[int]) -> None:
    n = len(arr)
    for i in range(n - 1):
        swapped = False
        for j in range(n - 1 - i):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                swapped = True
        if not swapped:
            break

🔍 Selection Sort — Sắp xếp chọn

Cơ chế: Tìm phần tử nhỏ nhất trong phần chưa sắp xếp, đổi chỗ với phần tử đầu tiên của phần chưa sắp xếp. Lặp lại cho đến hết.

Đặc điểm quan trọng:

  • NOT adaptive — luôn thực hiện O(n²) phép so sánh, kể cả khi mảng đã sắp xếp
  • NOT stable — thao tác swap có thể phá vỡ thứ tự tương đối của các phần tử bằng nhau
  • Minimum swaps — chỉ O(n) lần swap → lý tưởng khi ghi dữ liệu đắt (flash memory, EEPROM)

Tại sao Selection Sort không stable?

Ví dụ: [5a, 3, 5b, 2]       (5a và 5b là hai phần tử giá trị 5)

Bước 1: min = 2 (index 3)
  Swap arr[0] với arr[3] → [2, 3, 5b, 5a]
  → 5a nhảy ra sau 5b! Thứ tự tương đối bị phá vỡ.

Ban đầu: 5a trước 5b
Kết quả: 5b trước 5a  ← NOT STABLE ❌

Minh hoạ từng bước — [5, 1, 4, 2, 8]

Pass 0: [5, 1, 4, 2, 8]  → min=1(idx 1) → swap(0,1) → [1, 5, 4, 2, 8]  ✅
Pass 1: [1,|5, 4, 2, 8]  → min=2(idx 3) → swap(1,3) → [1, 2, 4, 5, 8]  ✅
Pass 2: [1, 2,|4, 5, 8]  → min=4(idx 2) → no swap   → [1, 2, 4, 5, 8]  ✅
Pass 3: [1, 2, 4,|5, 8]  → min=5(idx 3) → no swap   → [1, 2, 4, 5, 8]  ✅
Done!   [1, 2, 4, 5, 8]
         | = sorted boundary

Visualizer State Transitions — Selection Sort

VISUALIZER STATES — Selection Sort:
State 1: SCANNING   — vị trí đang quét highlight vàng
State 2: NEW_MIN    — tìm được min mới, highlight đỏ
State 3: SWAP       — đổi min vào biên sorted, animation cam
State 4: SETTLED    — phần tử ở vị trí sorted chuyển xanh lá
State 5: DONE       — tất cả xanh lá

Triển khai chuẩn

cpp
void selectionSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        if (minIdx != i) {
            swap(arr[i], arr[minIdx]);
        }
    }
}
java
public static void selectionSort(int[] arr) {
    int n = arr.length;
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        if (minIdx != i) {
            int temp = arr[i];
            arr[i] = arr[minIdx];
            arr[minIdx] = temp;
        }
    }
}
python
def selection_sort(arr: list[int]) -> None:
    n = len(arr)
    for i in range(n - 1):
        min_idx = i
        for j in range(i + 1, n):
            if arr[j] < arr[min_idx]:
                min_idx = j
        if min_idx != i:
            arr[i], arr[min_idx] = arr[min_idx], arr[i]

📥 Insertion Sort — Sắp xếp chèn

Cơ chế: Lấy từng phần tử từ phần chưa sắp xếp, dịch các phần tử lớn hơn sang phải, chèn vào đúng vị trí trong phần đã sắp xếp.

Đặc điểm — Vũ khí bí mật:

  • Adaptive — O(n) trên dữ liệu gần sorted (best case thực tế!)
  • Stable — bảo toàn thứ tự tương đối của phần tử bằng nhau
  • Online — có thể xử lý dữ liệu đến từng phần tử (streaming)
  • 🏭 Dùng trong Timsort — Python/Java sort dùng Insertion Sort cho subarray < 64 phần tử

Minh hoạ từng bước — [5, 1, 4, 2, 8]

Sorted | Unsorted        Action
─────────────────────────────────────────────────
[5]    | [1, 4, 2, 8]   key=1, shift 5→, insert 1 at [0]
[1, 5] | [4, 2, 8]      key=4, shift 5→, insert 4 at [1]
[1,4,5]| [2, 8]         key=2, shift 5→,4→, insert 2 at [1]
[1,2,4,5]| [8]          key=8, no shift, insert 8 at [4]
[1, 2, 4, 5, 8]         Done! ✅

Tại sao Insertion Sort = O(n) trên dữ liệu gần sorted?

Mảng gần sorted: [1, 2, 3, 5, 4, 6, 7, 8]
                              ↑ chỉ 1 phần tử sai vị trí

key=4: chỉ cần dịch [5] sang phải → insert 4 → xong
→ Mỗi phần tử chỉ cần 1-2 phép so sánh → tổng cộng ≈ O(n)

Visualizer State Transitions — Insertion Sort

VISUALIZER STATES — Insertion Sort:
State 1: PICK             — highlight phần tử đang chèn màu vàng
State 2: SHIFT            — các phần tử dịch phải, animation mũi tên
State 3: INSERT           — phần tử rơi vào gap đúng, chuyển xanh lá
State 4: SORTED_BOUNDARY  — ranh giới sorted/unsorted dịch phải
State 5: DONE             — tất cả xanh lá

Triển khai chuẩn

cpp
void insertionSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}
java
public static void insertionSort(int[] arr) {
    int n = arr.length;
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}
python
def insertion_sort(arr: list[int]) -> None:
    n = len(arr)
    for i in range(1, n):
        key = arr[i]
        j = i - 1
        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key

Khi nào O(n²) thắng O(n log n)?

💡 HPN Pro Tip — O(n²) không phải lúc nào cũng tệ

Nhiều anh em nghe "O(n²)" là nghĩ "chậm, tệ, không dùng". Sai lầm! Có những trường hợp O(n²) thắng O(n log n):

  1. Dữ liệu gần sorted → Insertion Sort chạy O(n), trong khi Quick Sort vẫn O(n log n)
  2. Mảng nhỏ (n < 20–50) → O(n²) có hằng số nhỏ hơn, ít overhead hơn (không recursion, không merge buffer)
  3. Cần stable + in-place → Insertion Sort là lựa chọn tự nhiên, không cần thêm bộ nhớ
  4. Tối thiểu ghi dữ liệu → Selection Sort chỉ O(n) swaps (tốt cho flash memory, EEPROM)
  5. Nền tảng tư duy → Hiểu O(n²) là điều kiện tiên quyết để hiểu tại sao O(n log n) quan trọng

Thực tế: Timsort (Python sort(), Java Arrays.sort()) dùng Insertion Sort cho các subarray nhỏ.

Operational Complexity — Con số thật

Đừng chỉ nói "O(n²)" — hãy hiểu nó nghĩa là gì với dữ liệu thực:

┌─────────────┬──────────────────────┬───────────────────────┐
│      n      │   O(n²) operations   │    Thời gian ước tính │
├─────────────┼──────────────────────┼───────────────────────┤
│      100    │          10,000      │    < 0.01 ms ✅       │
│    1,000    │       1,000,000      │    ~ 1 ms ✅          │
│   10,000    │     100,000,000      │    ~ 100 ms ⚠️       │
│  100,000    │  10,000,000,000      │    ~ 10 giây ❌       │
│ 1,000,000   │ 1,000,000,000,000   │    ~ timeout/crash 💀 │
└─────────────┴──────────────────────┴───────────────────────┘

So sánh với O(n log n):
┌─────────────┬──────────────────────┬───────────────────────┐
│      n      │ O(n log n) operations│    Thời gian ước tính │
├─────────────┼──────────────────────┼───────────────────────┤
│    1,000    │          10,000      │    ~ 0.01 ms ✅       │
│   10,000    │         133,000      │    ~ 0.1 ms ✅        │
│  100,000    │       1,700,000      │    ~ 1.7 ms ✅        │
│ 1,000,000   │      20,000,000      │    ~ 20 ms ✅         │
└─────────────┴──────────────────────┴───────────────────────┘

⚠️ Quy tắc ngón tay cái

  • n ≤ 1,000 → O(n²) hoàn toàn ổn, không cần tối ưu
  • 1,000 < n ≤ 10,000 → O(n²) bắt đầu chậm, cân nhắc O(n log n)
  • n > 10,000 → Bắt buộc dùng O(n log n) hoặc tốt hơn
  • Trong API/backend → Luôn giả định n có thể lớn, dùng O(n log n) mặc định

Sai lầm điển hình

⚠️ Cạm bẫy

Sai lầm 1: "Bubble Sort luôn là O(n²)"

Sai: Nhiều bạn nghĩ Bubble Sort không thể nhanh hơn O(n²)

Đúng: Với cờ swapped, Bubble Sort đạt O(n) trên mảng đã sắp xếp

python
# ❌ Naive — luôn O(n²), kể cả mảng đã sorted
def bubble_naive(arr):
    n = len(arr)
    for i in range(n - 1):
        for j in range(n - 1 - i):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]

# ✅ Optimized — O(n) best case với early exit
def bubble_optimized(arr):
    n = len(arr)
    for i in range(n - 1):
        swapped = False
        for j in range(n - 1 - i):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                swapped = True
        if not swapped:
            break  # Không có swap → mảng đã sorted

⚠️ Cạm bẫy

Sai lầm 2: "Selection Sort is stable"

Sai: Nhiều bạn nhầm Selection Sort là stable vì "chỉ swap ít"

Đúng: Selection Sort KHÔNG stable — swap phá vỡ thứ tự tương đối

Ví dụ: [3a, 2, 3b, 1]
Pass 0: min = 1 → swap(arr[0], arr[3]) → [1, 2, 3b, 3a]
→ 3a giờ đứng SAU 3b → thứ tự bị phá vỡ ❌

⚠️ Cạm bẫy

Sai lầm 3: Off-by-one trong Bubble Sort

Sai: Vòng lặp trong dùng j < n hoặc j < n - 1

Đúng: Phải là j < n - 1 - i — vì sau mỗi pass, phần tử cuối đã settled

python
# ❌ Sai: truy cập out of bounds hoặc so sánh thừa
for j in range(n - 1):         # Thiếu "- i" → so sánh phần tử đã settled
    if arr[j] > arr[j + 1]: ...

# ✅ Đúng: thu hẹp biên sau mỗi pass
for j in range(n - 1 - i):    # Đúng boundary
    if arr[j] > arr[j + 1]: ...

⚠️ Cạm bẫy

Sai lầm 4: Dùng >= thay vì > trong Insertion Sort

Sai: while j >= 0 and arr[j] >= key — phá vỡ tính stable

Đúng: while j >= 0 and arr[j] > key — bảo toàn thứ tự phần tử bằng nhau

python
# ❌ Sai: dùng >= phá vỡ stability
while j >= 0 and arr[j] >= key:  # Phần tử bằng nhau cũng bị dịch
    arr[j + 1] = arr[j]
    j -= 1

# ✅ Đúng: dùng > để giữ stable
while j >= 0 and arr[j] > key:   # Phần tử bằng nhau giữ nguyên vị trí
    arr[j + 1] = arr[j]
    j -= 1

⚠️ Cạm bẫy

Sai lầm 5: "O(n²) luôn tệ, không bao giờ dùng"

Sai: Loại bỏ hoàn toàn O(n²) khỏi đầu

Đúng: O(n²) tối ưu cho n nhỏ, dữ liệu gần sorted, và khi cần stable + in-place. Timsort — thuật toán sort mặc định của Python và Java — dùng Insertion Sort cho subarray < 64 phần tử.

Bài tập luyện tập

🧠 Quiz — Kiểm tra hiểu biết

🧠 Quiz

Câu hỏi: Thuật toán nào vừa stable VÀ adaptive?

  • [ ] A. Bubble Sort — stable nhưng chỉ adaptive khi có optimization flag
  • [ ] B. Selection Sort — không stable, không adaptive
  • [x] C. Insertion Sort — stable và adaptive tự nhiên, không cần flag
  • [ ] D. Cả 3 đều stable

💡 Giải thích: Insertion Sort tự nhiên là adaptive — trên mảng gần sorted, vòng while gần như không chạy nên đạt O(n). Nó cũng stable vì chỉ dịch phần tử khi arr[j] > key (strict greater), giữ nguyên thứ tự phần tử bằng nhau. Bubble Sort cần thêm cờ swapped mới adaptive, còn Selection Sort không bao giờ adaptive.

🐛 Spot the Bug — Tìm lỗi

Đoạn Insertion Sort bên dưới có một lỗi nghiêm trọng. Bạn tìm được không?

python
def insertion_sort_buggy(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i  # 🐛 Bug ở đây!
        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key
💡 Gợi ý

Hãy trace qua mảng [3, 1, 2] với đoạn code trên. Khi i = 1, key = 1, j bắt đầu từ đâu? Phần tử arr[j] so sánh với key — có hợp lý không?

✅ Lời giải

Bug: j = i thay vì j = i - 1

Khi j = i, dòng arr[j] > key so sánh arr[i] > key, tức là key > keyluôn false → vòng while không bao giờ chạy → mảng không được sắp xếp!

Ngoài ra, arr[j + 1] = key sẽ ghi ra ngoài mảng khi j = n - 1.

python
# ✅ Fix: j phải bắt đầu từ i - 1
def insertion_sort_fixed(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i - 1  # ✅ Bắt đầu từ phần tử TRƯỚC key
        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key

Bài học: Luôn trace code bằng tay với mảng nhỏ trước khi tin rằng code đúng.

🧩 Parsons Problem — Sắp xếp dòng code

Sắp xếp các dòng code sau thành hàm Insertion Sort hoàn chỉnh:

Các dòng (đã xáo trộn):
A: arr[j + 1] = key
B: while j >= 0 and arr[j] > key:
C: for i in range(1, len(arr)):
D: j = i - 1
E: arr[j + 1] = arr[j]
F: key = arr[i]
G: j -= 1
💡 Gợi ý
  • Vòng for đi trước mọi thứ bên trong
  • Trước khi dịch, cần biết key là gì và j bắt đầu từ đâu
  • Vòng while chứa thao tác dịch phải
  • Cuối cùng là chèn key vào vị trí đúng
✅ Đáp án
python
# Thứ tự đúng: C → F → D → B → E → G → A
def insertion_sort(arr):
    for i in range(1, len(arr)):        # C
        key = arr[i]                     # F
        j = i - 1                        # D
        while j >= 0 and arr[j] > key:  # B
            arr[j + 1] = arr[j]          # E
            j -= 1                       # G
        arr[j + 1] = key                 # A

Giải thích luồng: Vòng for (C) duyệt từ phần tử thứ 2. Lưu key (F) và bắt đầu j từ trước key (D). Vòng while (B) dịch phải (E, G) các phần tử lớn hơn key. Cuối cùng chèn key (A) vào gap.

Checklist ghi nhớ

✅ Checklist triển khai

Bubble Sort

  • [ ] Adjacent swap — so sánh hai phần tử kề, đổi nếu sai thứ tự
  • [ ] Cờ swapped → best case O(n), không có cờ → luôn O(n²)
  • [ ] Stable sort — phần tử bằng nhau giữ thứ tự ban đầu
  • [ ] In-place — O(1) bộ nhớ phụ

Selection Sort

  • [ ] Find-min + swap — tìm min trong phần unsorted, đổi vào đầu
  • [ ] Luôn O(n²) so sánh — KHÔNG adaptive
  • [ ] Chỉ O(n) swaps — tối ưu khi ghi dữ liệu đắt
  • [ ] KHÔNG stable — swap phá vỡ thứ tự tương đối

Insertion Sort

  • [ ] Shift-and-insert — dịch phải rồi chèn vào gap
  • [ ] O(n) trên dữ liệu gần sorted — adaptive tự nhiên
  • [ ] Stable — dùng > (strict), không dùng >=
  • [ ] Được Timsort sử dụng cho subarray nhỏ (< 64 phần tử)

Quy tắc chung

  • [ ] O(n²) ổn cho n < 50 và dữ liệu gần sorted
  • [ ] Hiểu tại sao thuật toán là O(n²), không chỉ biết nó là O(n²)
  • [ ] Nắm rõ stability, adaptivity, và số lần swap khi phỏng vấn

Liên kết học tiếp

📍 Trang tiếp theo

TrangMô tả
Sắp Xếp Hiệu QuảMerge Sort, Quick Sort, Heap Sort + Timsort
Bản Đồ Phase 1Quay lại lộ trình tổng quan