Sắp xếp Tim
Trang này giới thiệu Timsort, một thuật toán sắp xếp lai, ổn định.
Giới thiệu
Timsort do Tim Peters (lõi phát triển Python) thiết kế năm 2002 và được ứng dụng trong Python. Nó kết hợp lợi thế của insertion sort và merge sort, tận dụng các đoạn con đã có trật tự trong dữ liệu, nên đặc biệt hiệu quả với dữ liệu chứa nhiều đoạn đã có thứ tự. Kể từ Python 2.3, Timsort là thuật toán sắp xếp mặc định của thư viện chuẩn Python và cũng được dùng ở nhiều môi trường khác (ví dụ Java SE 7 cho mảng đối tượng).
Các bước
Ý tưởng chính của Timsort là nhận dạng và tận dụng độ có trật tự trong dữ liệu; các bước chính:
- Nhận dạng Run: quét mảng để tìm các đoạn con đã có thứ tự (Run).
- Mở rộng Run: nếu độ dài Run < MIN_RUN thì dùng insertion sort mở rộng Run.
- Gộp Run: duy trì một ngăn xếp các Run và theo luật hợp nhất để ghép các Run thành đoạn lớn hơn.
Nhận dạng Run
Timsort quét từ trái sang phải để nhận Run liên tiếp: - Run tăng: nếu phần tử sau >= phần tử trước thì tiếp tục mở rộng. - Run giảm: nếu phần tử sau < phần tử trước thì tiếp tục mở rộng, sau đó đảo chiều Run thành tăng.
Mở rộng Run
Để tối ưu cho dữ liệu nhỏ, Timsort có một ngưỡng độ dài Run tối thiểu MIN_RUN (thường tính theo độ dài mảng, nằm trong khoảng 32–64).
- Nếu Run đã >= MIN_RUN thì push vào ngăn xếp.
- Nếu Run < MIN_RUN thì dùng binary insertion sort để mở rộng Run đến MIN_RUN rồi push.
Gộp Run
Timsort quản lý việc gộp Run bằng một ngăn xếp và tuân thủ các quy tắc gộp đặc biệt để giữ cân bằng và tính ổn định.
Quy tắc gộp
Timsort là thuật toán ổn định; khi gộp chỉ gộp các Run kề nhau để không làm xáo trộn thứ tự tương đối của các phần tử bằng nhau.
Để đảm bảo cân bằng, trước mỗi lần gộp thuật toán kiểm tra ba Run trên đỉnh ngăn xếp X, Y, Z và yêu cầu: - Điều kiện 1: len(Z) > len(Y) + len(X) - Điều kiện 2: len(Y) > len(X)
Nếu ba Run trên đỉnh không thỏa, Timsort sẽ gộp Y với X hoặc Z (theo phần nhỏ hơn) rồi tiếp tục kiểm tra lại. Khi thỏa, tiếp tục push Run mới và lặp.

Tối ưu khi gộp
Để giảm công việc khi gộp hai Run có độ dài chênh lệch, Timsort dùng binary search để xác định chính xác phạm vi cần gộp, chỉ xử lý phần tử cần di chuyển.
Ngoài ra, Timsort sao chép Run ngắn hơn vào bộ đệm tạm rồi trộn dần trở lại mảng chính để giảm thao tác di chuyển.
Ví dụ: Run A = [1,2,3,6,10], Run B = [4,5,7,9,12,14,17] - Bằng binary search, xác định vị trí chèn của 4 trong A (vị trí thứ 4), vị trí chèn của 10 trong B (vị trí thứ 5). - Do đó phần A trước 3 phần tử và phần B sau 3 phần tử đã đúng vị trí; chỉ cần gộp A phần [6,10] với B phần [4,5,7,9] như hình:
Chế độ tăng tốc (Galloping Mode)
Khi một phía liên tục “thắng” nhiều lần trong quá trình gộp, Timsort bật chế độ galloping để tăng tốc: 1. Dùng exponential search (bước 1,2,4,8,…) trên phía đang thắng để tìm khoảng chứa vị trí chèn. 2. Sau đó dùng binary search trong khoảng đó để xác định chính xác.
Timsort giữ tham số Min_Gallop (mặc định 7) và điều chỉnh động: nếu galloping hiệu quả giảm Min_Gallop đi 1, nếu không hiệu quả tăng Min_Gallop đi 1. Nhờ vậy, với dữ liệu gần như đã có thứ tự Timsort có thể đạt gần \(O(n)\); với dữ liệu ngẫu nhiên, nó sẽ nghiêng về merge thông thường giữ \(O(n\log n)\).
Độ phức tạp
Độ phức tạp của Timsort phụ thuộc vào mức độ trật tự của dữ liệu:
- Tốt nhất: \(O(n)\) khi dữ liệu đã hoặc gần như đã sắp xếp.
- Tệ nhất: \(O(n\log n)\) khi dữ liệu hoàn toàn ngẫu nhiên (run độ dài ~1).
Chứng minh tóm tắt:
- Nhận dạng và mở rộng Run: quét mảng một lần => \(O(n)\).
- Gộp Run: số Run ở worst-case là \(O(n)\) (với MIN_RUN là hằng số), số lần gộp tương đương \(O(\log n)\), mỗi lần gộp tốn \(O(n)\) tổng => \(O(n\log n)\).
Không gian: Timsort cần thêm \(O(n)\) bộ đệm và ngăn xếp Run, nên không gian phụ là \(O(n)\).
Cài đặt
Ví dụ: giả mã
Tài liệu tham khảo
Last updated on this page:, Update history
Found an error? Want to help improve? Edit this page on GitHub!
Contributors to this page:OI-wiki
All content on this page is provided under the terms of the CC BY-SA 4.0 and SATA license, additional terms may apply