Tại sao Collections.sort sử dụng sắp xếp hợp nhất thay vì nhanh chóng?


101

Chúng tôi biết rằng sắp xếp nhanh là thuật toán sắp xếp nhanh nhất.

JDK6 collections.sortsử dụng thuật toán sắp xếp hợp nhất thay vì sắp xếp nhanh. Nhưng Arrays.sort sử dụng thuật toán sắp xếp nhanh.

Lý do Collections.sort sử dụng sắp xếp hợp nhất thay vì sắp xếp nhanh là gì?


3
Trừ khi bạn có thể nhờ một tác giả JDK trả lời, tất cả những gì bạn sẽ nhận được là phỏng đoán. Không phải là một câu hỏi thực sự.
Marquis of Lorne,

4
@EJP Điểm tốt, nhưng chắc chắn "Không mang tính xây dựng" là lý do đóng cửa đúng. Tôi đã rõ câu hỏi ở đây là gì.
Duncan Jones

2
Bởi vì những người Java đã quyết định làm điều đó như thế này. Hỏi họ. Tôi nghĩ bạn không thể nhận được câu trả lời chính đáng ở đây. Và sắp xếp nhanh không phải là tốt nhất. Nó chỉ là tốt nhất để sử dụng chung .
Adam Arold

4
Một phỏng đoán: Quicksort không ổn định, Mergesort là. Đối với nguyên thủy, một loại ổn định / không ổn định là không liên quan, đối với các đối tượng, nó có thể là như vậy (hoặc ít nhất, bạn có thể nhận được lỗi đối với một loại không ổn định).
parsifal

2
@EJP, Không có gì ngăn cản ý định công khai của các tác giả JDK. Một khi nó được công khai, chúng tôi không cần chính tác giả trả lời. Trên thực tế, có thể nhận được một câu trả lời khó đoán ngay cả khi không có tác giả JDK trả lời.
Pacerier

Câu trả lời:


187

Rất có thể từ Josh Bloch § :

Tôi đã viết những phương pháp này, vì vậy tôi cho rằng tôi đủ điều kiện để trả lời. Đúng là không có một thuật toán sắp xếp nào tốt nhất. QuickSort có hai thiếu sót lớn khi so sánh với mergesort:

  1. Nó không ổn định (như parsifal đã lưu ý).

  2. Nó không đảm bảo hiệu suất n log n; nó có thể suy giảm hiệu suất bậc hai đối với các yếu tố đầu vào bệnh lý.

Tính ổn định không phải là vấn đề đối với các kiểu nguyên thủy, vì không có khái niệm đồng nhất khác biệt với bình đẳng (giá trị). Và khả năng xảy ra hành vi bậc hai được coi là không phải là vấn đề trong thực tế đối với việc triển khai của Bentely và McIlroy (hoặc sau đó là đối với Dual Pivot Quicksort ), đó là lý do tại sao các biến thể QuickSort này được sử dụng cho các loại nguyên thủy.

Tính ổn định là một vấn đề lớn khi phân loại các đối tượng tùy ý. Ví dụ: giả sử bạn có các đối tượng đại diện cho thư email và bạn sắp xếp chúng trước tiên theo ngày, sau đó theo người gửi. Bạn mong đợi chúng được sắp xếp theo ngày trong mỗi người gửi, nhưng điều đó sẽ chỉ đúng nếu việc sắp xếp ổn định. Đó là lý do tại sao chúng tôi đã chọn cung cấp một sắp xếp ổn định (Merge Sort) để sắp xếp các tham chiếu đối tượng. (Nói một cách dễ hiểu, nhiều sắp xếp ổn định tuần tự dẫn đến thứ tự từ vựng trên các khóa theo thứ tự ngược lại của các loại: sắp xếp cuối cùng xác định khóa con quan trọng nhất.)

Đó là một lợi ích phụ tuyệt vời mà Merge Sort đảm bảo hiệu suất n log n (thời gian) bất kể đầu vào là gì. Tất nhiên có một mặt trái: sắp xếp nhanh là một loại "tại chỗ": nó chỉ yêu cầu ghi lại n không gian bên ngoài (để duy trì ngăn xếp cuộc gọi). Mặt khác, hợp nhất, sắp xếp, yêu cầu O (n) không gian bên ngoài. Biến thể TimSort (được giới thiệu trong Java SE 6) yêu cầu ít không gian hơn đáng kể (O (k)) nếu mảng đầu vào gần như được sắp xếp.

Ngoài ra, những điều sau đây có liên quan:

Thuật toán được sử dụng bởi java.util.Arrays.sort và (gián tiếp) bởi java.util.Collections.sort để sắp xếp các tham chiếu đối tượng là một "hợp nhất được sửa đổi (trong đó hợp nhất bị bỏ qua nếu phần tử cao nhất trong danh sách con thấp hơn phần tử thấp nhất trong danh sách phụ cao). " Nó là một kiểu sắp xếp ổn định nhanh hợp lý đảm bảo hiệu suất O (n log n) và yêu cầu thêm O (n) không gian. Vào thời của nó (nó được viết vào năm 1997 bởi Joshua Bloch), đó là một lựa chọn tốt, nhưng ngày nay nhưng chúng tôi có thể làm tốt hơn nhiều.

Kể từ năm 2003, sắp xếp danh sách của Python đã sử dụng một thuật toán được gọi là timsort (sau Tim Peters, người đã viết nó). Nó là một hợp nhất lặp đi lặp lại, ổn định, thích ứng, yêu cầu ít hơn n log (n) so sánh khi chạy trên các mảng được sắp xếp một phần, đồng thời cung cấp hiệu suất tương đương với hợp nhất truyền thống khi chạy trên các mảng ngẫu nhiên. Giống như tất cả các hợp nhất thích hợp, timsort ổn định và chạy trong thời gian O (n log n) (trường hợp xấu nhất). Trong trường hợp xấu nhất, timsort yêu cầu không gian lưu trữ tạm thời cho n / 2 tham chiếu đối tượng; trong trường hợp tốt nhất, nó chỉ yêu cầu một lượng nhỏ không gian cố định. Ngược lại điều này với việc triển khai hiện tại, luôn yêu cầu thêm không gian cho n tham chiếu đối tượng và chỉ đánh bại n log n trên danh sách gần như được sắp xếp.

Timsort được mô tả chi tiết tại đây: http://svn.python.org/projects/python/trunk/Objects/listsort.txt .

Việc triển khai ban đầu của Tim Peters được viết bằng C. Joshua Bloch đã chuyển nó từ C sang Java và kết thúc kiểm tra, đánh giá chuẩn và điều chỉnh rộng rãi mã kết quả. Mã kết quả là một phần thay thế cho java.util.Arrays.sort. Trên dữ liệu có thứ tự cao, mã này có thể chạy nhanh gấp 25 lần so với quá trình triển khai hiện tại (trên máy chủ HotSpot VM). Trên dữ liệu ngẫu nhiên, tốc độ của triển khai cũ và mới có thể so sánh được. Đối với danh sách rất ngắn, việc triển khai mới nhanh hơn đáng kể so với danh sách cũ ngay cả trên dữ liệu ngẫu nhiên (vì nó tránh sao chép dữ liệu không cần thiết).

Ngoài ra, hãy xem Java 7 có sử dụng Sắp xếp theo Tim cho Mảng Phương thức. Sắp xếp? .

Không có một sự lựa chọn "tốt nhất" nào. Cũng như nhiều thứ khác, đó là về sự cân bằng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.