Menu
CoddyTech

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ạpGhi chú
Tốt nhấtO(n)Đã sắp xếp sẵn
Trung bìnhO(n²)Thứ tự ngẫu nhiên
Xấu nhấtO(n²)Sắp xếp ngược
Không gianO(1)Sắp xếp tại chỗ
Ổn địnhCó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
1Coi phần tử đầu tiên là vùng đã sắp xếp có kích thước một.
2Lấy phần tử tiếp theo làm khóa.
3Dời mọi phần tử đã sắp xếp lớn hơn khóa sang phải một ô.
4Chèn khóa vào chỗ trống vừa mở.
5Lặ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ượtMảngThao 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 khiTrá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

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)
Chạy code này trong Playground Python

Câ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?
Insertion sort tốn 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?
Cả hai đều là thuật toán sắp xếp so sánh 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ỏ?
Insertion sort có hệ số hằng rất thấp, không đệ quy và không cấp phát thêm bộ nhớ, nên với dữ liệu nhỏ nó thắng các thuật toán 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?
Insertion sort thường được viết cho mảng, nơi việc dời phần tử là chi phí chính. Với danh sách liên kết, bạn tránh được việc dời bằng cách nối nút vào đúng chỗ, nhưng lại mất khả năng truy cập ngẫu nhiên nhanh, nên tìm vị trí chèn vẫn tốn thời gian tuyến tính cho mỗi phần tử và tổng chi phí vẫn là O(n²).
Hình minh họa các ngôn ngữ lập trình của Coddy

Thành thạo thuật toán cùng Coddy

Bắt đầu