Insertion sort (sắp xếp chèn)
Cập nhật lần cuối
Insertion sort (sắp xếp chèn) xây dựng mảng đã sắp xếp từng phần tử một. Thuật toán lấy phần tử chưa sắp xếp tiếp theo (gọi là "khóa") và dời mọi phần tử lớn hơn trong vùng đã sắp xếp sang phải một ô, rồi đặt khóa vào chỗ trống. Đây chính là cách phần lớn mọi người xếp một tay bài. Nhấn Phát ở trên để xem từng khóa được chèn vào, hoặc chạy từng bước để xem từng lần dời.
Insertion sort rất nhanh với dữ liệu nhỏ hoặc gần như đã sắp xếp: nó chạy trong O(n) khi dữ liệu đã có thứ tự, vì vậy nhiều thuật toán sắp xếp lai chuyển sang dùng nó cho các mảng con nhỏ.
Độ 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) | Đã sắp xếp sẵn |
| 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ỗ |
| Ổn định | Có | Các phần tử bằng nhau giữ nguyên thứ tự tương đối |
Từng bước
| Bước | Điều gì xảy ra |
|---|---|
| 1 | Coi phần tử đầu tiên là vùng đã sắp xếp có kích thước một. |
| 2 | Lấy phần tử tiếp theo làm khóa. |
| 3 | Dời mọi phần tử đã sắp xếp lớn hơn khóa sang phải một ô. |
| 4 | Chèn khóa vào chỗ trống vừa mở. |
| 5 | Lặp lại cho đến khi mọi phần tử đều đã được chèn. |
Ví dụ minh họa
Sắp xếp [5, 2, 4, 1]:
| Lượt | Mảng | Thao tác |
|---|---|---|
| Bắt đầu | [5, 2, 4, 1] | 5 là vùng đã sắp xếp ban đầu, có kích thước một. |
| 1 | [2, 5, 4, 1] | Khóa 2: dời 5 sang phải, chèn 2 vào đầu. |
| 2 | [2, 4, 5, 1] | Khóa 4: dời 5 sang phải, 2 nhỏ hơn nên dừng, chèn 4. |
| 3 | [1, 2, 4, 5] | Khóa 1: dời 5, 4, 2 sang phải, chèn 1 vào đầu. |
| Xong | [1, 2, 4, 5] | Mọi phần tử đã được chèn; mảng đã được sắp xếp. |
Khi nào nên dùng insertion sort
| Nên dùng khi | Tránh dùng khi |
|---|---|
Mảng nhỏ (khoảng n < 20). | Mảng lớn và có thứ tự ngẫu nhiên. |
Dữ liệu đã gần như được sắp xếp, cho trường hợp tốt nhất O(n). | Bạn cần bảo đảm trường hợp xấu nhất là O(n log n). |
Bạn cần một thuật toán ổn định, tại chỗ, chỉ tốn O(1) bộ nhớ phụ. | Việc di chuyển phần tử tốn kém, vì thuật toán dời phần tử rất nhiều lần. |
| Dữ liệu đến dần dần và phải luôn được giữ có thứ tự. | Dữ liệu đầu vào bị sắp xếp ngược, trường hợp xấu nhất O(n²) của nó. |
Code Insertion Sort
Một cách cài đặt Insertion 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 Insertion Sort bằng Python
1def insertion_sort(a):2 for i in range(1, len(a)):3 key = a[i]4 j = i - 15 # Shift larger elements one slot to the right6 while j >= 0 and a[j] > key:7 a[j + 1] = a[j]8 j -= 19 a[j + 1] = key10 return a11
12
13nums = [7, 3, 9, 1, 5, 8, 2]14print("Before:", nums)15insertion_sort(nums)16print("After: ", nums)Code Insertion Sort bằng JavaScript
1function insertionSort(a) {2 for (let i = 1; i < a.length; i++) {3 const key = a[i];4 let j = i - 1;5 // Shift larger elements right to open a slot for key6 while (j >= 0 && a[j] > key) {7 a[j + 1] = a[j];8 j--;9 }10 a[j + 1] = key;11 }12 return a;13}14
15const data = [5, 2, 9, 1, 7, 3];16console.log("Before:", data);17console.log("Sorted:", insertionSort([...data]));Code Insertion Sort bằng Java
1import java.util.Arrays;2
3public class Main {4 static void insertionSort(int[] arr) {5 for (int i = 1; i < arr.length; i++) {6 int key = arr[i];7 int j = i - 1;8 // Shift larger elements one slot to the right9 while (j >= 0 && arr[j] > key) {10 arr[j + 1] = arr[j];11 j--;12 }13 arr[j + 1] = key;14 }15 }16
17 public static void main(String[] args) {18 int[] arr = {7, 3, 9, 1, 5, 8, 2};19 System.out.println("Before: " + Arrays.toString(arr));20 insertionSort(arr);21 System.out.println("After: " + Arrays.toString(arr));22 }23}Code Insertion Sort bằng C++
1#include <iostream>2#include <vector>3
4void printVec(const std::vector<int>& a) {5 for (int x : a) std::cout << x << " ";6 std::cout << "\n";7}8
9void insertionSort(std::vector<int>& a) {10 for (size_t i = 1; i < a.size(); ++i) {11 int key = a[i];12 int j = static_cast<int>(i) - 1;13 // Shift larger elements one slot to the right14 while (j >= 0 && a[j] > key) {15 a[j + 1] = a[j];16 --j;17 }18 a[j + 1] = key;19 }20}21
22int main() {23 std::vector<int> data = {7, 3, 9, 1, 5, 8, 2};24 std::cout << "Before: ";25 printVec(data);26 insertionSort(data);27 std::cout << "After: ";28 printVec(data);29 return 0;30}Code Insertion Sort bằng C
1#include <stdio.h>2
3void printArr(const int a[], int n) {4 for (int i = 0; i < n; i++) printf("%d ", a[i]);5 printf("\n");6}7
8void insertionSort(int a[], int n) {9 for (int i = 1; i < n; i++) {10 int key = a[i];11 int j = i - 1;12 // Shift larger elements one slot to the right13 while (j >= 0 && a[j] > key) {14 a[j + 1] = a[j];15 j--;16 }17 a[j + 1] = key;18 }19}20
21int main(void) {22 int data[] = {7, 3, 9, 1, 5, 8, 2};23 int n = sizeof(data) / sizeof(data[0]);24 printf("Before: ");25 printArr(data, n);26 insertionSort(data, n);27 printf("After: ");28 printArr(data, n);29 return 0;30}Code Insertion 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 key : INTEGER14
15// Insert each value into the sorted part on its left16FOR i ← 2 TO n17 key ← nums[i]18 j ← i - 119 WHILE j > 0 AND nums[j] > key DO20 nums[j + 1] ← nums[j]21 j ← j - 122 ENDWHILE23 nums[j + 1] ← key24NEXT i25
26FOR i ← 1 TO n27 OUTPUT nums[i]28NEXT iCâu hỏi thường gặp về insertion sort
Độ phức tạp thời gian của insertion sort là bao nhiêu?
O(n²) trong trường hợp trung bình và xấu nhất, nhưng chỉ O(n) với mảng đã sắp xếp hoặc gần như đã sắp xếp. Thuật toán dùng O(1) bộ nhớ phụ.Insertion sort có ổn định không?
Có. Insertion sort chỉ dời những phần tử lớn hơn hẳn khóa, nên các phần tử bằng nhau không bao giờ vượt qua nhau và thứ tự tương đối của chúng được giữ nguyên.
Khi nào nên dùng insertion sort?
Hãy dùng nó cho mảng nhỏ hoặc dữ liệu đã gần như được sắp xếp. Nhờ chi phí phụ thấp và trường hợp tốt nhất thích ứng, các thuật toán lai như Timsort dùng nó cho những đoạn nhỏ.
Insertion sort khác bubble sort như thế nào?
O(n²), nhưng insertion sort dời phần tử để mở chỗ trống cho khóa, còn bubble sort liên tục đổi chỗ các cặp phần tử kề nhau bị sai thứ tự. Insertion sort 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ó đạt trường hợp tốt nhất O(n).Tại sao insertion sort nhanh hơn merge sort với mảng nhỏ?
O(n log n) dù độ phức tạp tiệm cận kém hơn. Đó chính là lý do các thuật toán lai như Timsort và introsort chuyển sang insertion sort cho các mảng con nhỏ.Insertion sort chạy tốt hơn với danh sách liên kết hay với mảng?
O(n²).