Tại sao phương pháp Arrays.sort của Java sử dụng hai thuật toán sắp xếp khác nhau cho các kiểu khác nhau?


121

Arrays.sortPhương thức của Java 6 sử dụng Quicksort cho các mảng nguyên thủy và sắp xếp hợp nhất cho các mảng đối tượng. Tôi tin rằng hầu hết thời gian Quicksort nhanh hơn sắp xếp hợp nhất và tốn ít bộ nhớ hơn. Các thí nghiệm của tôi hỗ trợ điều đó, mặc dù cả hai thuật toán đều là O (n log (n)). Vậy tại sao các thuật toán khác nhau được sử dụng cho các loại khác nhau?


14
Trường hợp xấu nhất của Quicksort là N ^ 2 không phải NlogN.
codaddict 14/09/10

Chờ đã, điều gì sẽ xảy ra nếu bạn có một mảng Integers hoặc một cái gì đó?
Tikhon Jelvis

1
Điều này không được giải thích trong nguồn bạn đọc?
Humphrey Bogart

5
Thông tin này không còn hiện tại. Bắt đầu từ Java SE 7, MergeSort đã được thay thế bằng TimSortQuickSort được thay thế bằng Dual-Pivot QuickSort . Xem câu trả lời của tôi bên dưới để biết các liên kết đến tài liệu Java API.
Will Byrne

Câu trả lời:


200

Lý do có thể xảy ra nhất: quicksort không ổn định , tức là các mục bằng nhau có thể thay đổi vị trí tương đối của chúng trong khi sắp xếp; trong số những thứ khác, điều này có nghĩa là nếu bạn sắp xếp một mảng đã được sắp xếp, nó có thể không thay đổi.

Vì các kiểu nguyên thủy không có danh tính (không có cách nào để phân biệt hai int có cùng giá trị), điều này không quan trọng đối với chúng. Nhưng đối với các loại tham chiếu, nó có thể gây ra sự cố cho một số ứng dụng. Do đó, một sắp xếp hợp nhất ổn định được sử dụng cho những.

OTOH, một lý do để không sử dụng sắp xếp hợp nhất ổn định (đảm bảo n * log (n)) cho các kiểu nguyên thủy có thể là nó yêu cầu tạo một bản sao của mảng. Đối với các kiểu tham chiếu, trong đó các đối tượng được tham chiếu thường chiếm nhiều bộ nhớ hơn mảng tham chiếu, điều này thường không quan trọng. Nhưng đối với các kiểu nguyên thủy, việc sao chép hoàn toàn mảng sẽ tăng gấp đôi mức sử dụng bộ nhớ.


1
Một lý do khác để sử dụng quicksort là trong trường hợp trung bình, quicksort nhanh hơn mergesort. Mặc dù quicksort thực hiện nhiều phép so sánh hơn so với merge, nó thực hiện ít truy cập mảng hơn nhiều. Quicksort 3 chiều cũng có thể đạt được thời gian tuyến tính nếu đầu vào chứa nhiều mục nhập trùng lặp, điều này không có gì lạ trong các ứng dụng thực tế (Tôi đoán là sắp xếp nhanh trục kép cũng có thuộc tính này).
Jingguo Yao

Đối với các loại nguyên thủy nó không sao chép mảng, nó có thể sắp xếp chúng tại chỗ, vì vậy tôi nghĩ rằng lý do duy nhất là hợp đồng ổn định, về cơ bản ...
rogerdpack

27

Theo tài liệu Java 7 API được trích dẫn trong câu trả lời này , Arrays#Sort()đối với mảng đối tượng hiện sử dụng TimSort , là sự kết hợp của MergeSort và InsertionSort. Mặt khác, Arrays#sort()đối với các mảng nguyên thủy hiện sử dụng Dual-Pivot QuickSort . Những thay đổi này đã được thực hiện bắt đầu trong Java SE 7.


2
Nó không phải là một câu trả lời, tại sao 2 thuật toán khác nhau đã được chọn.
Alexandr

12

Một lý do mà tôi có thể nghĩ đến là quicksort có độ phức tạp thời gian trong trường hợp xấu nhất là O ( n ^ 2 ) trong khi mergesort giữ lại thời gian trong trường hợp xấu nhất là O ( n log n ). Đối với mảng đối tượng, có một kỳ vọng hợp lý rằng sẽ có nhiều tham chiếu đối tượng trùng lặp, đây là một trường hợp mà quicksort hoạt động tồi tệ nhất.

Có một so sánh trực quan phù hợp về các thuật toán khác nhau , đặc biệt chú ý đến biểu đồ ngoài cùng bên phải cho các thuật toán khác nhau.


2
Java quicksort là một quicksort được sửa đổi không hạ cấp thành O (n ^ 2), từ tài liệu "Thuật toán này cung cấp hiệu suất n * log (n) trên nhiều tập dữ liệu khiến các nhanh chóng khác giảm xuống hiệu suất bậc hai"
sbridges

7

Tôi đang tham gia lớp Coursera về Thuật toán và trong một bài giảng, Giáo sư Bob Sedgewick đã đề cập đến việc đánh giá sắp xếp hệ thống Java:

"Nếu một lập trình viên đang sử dụng các đối tượng, có thể không gian không phải là yếu tố quan trọng cần cân nhắc và không gian thừa được sử dụng bởi sắp xếp hợp nhất có thể không phải là vấn đề. Và nếu một lập trình viên đang sử dụng các kiểu nguyên thủy, có lẽ hiệu suất là điều quan trọng nhất để họ sử dụng sắp xếp nhanh chóng."


4
Nó không phải là lý do chính. Ngay sau câu đó, có một câu hỏi, được nhúng vào video về "Tại sao MergeSort loại tham chiếu được sử dụng?" (vì nó ổn định). Tôi nghĩ Sedgewick đã không đề cập đến điều đó trong video để để nó câu hỏi.
likern

1

java.util.Arrays sử dụng quicksort cho các kiểu nguyên thủy như int và mergesort cho các đối tượng triển khai So sánh hoặc sử dụng Bộ so sánh . Ý tưởng của việc sử dụng hai phương pháp khác nhau là nếu một lập trình viên sử dụng các đối tượng, thì không gian có thể không phải là vấn đề quan trọng nhất và vì vậy không gian bổ sung được sử dụng bởi mergesort có thể không phải là vấn đề và nếu lập trình viên sử dụng các kiểu nguyên thủy có thể hiệu suất là điều quan trọng nhất vì vậy hãy sử dụng các quicksort .

Ví dụ: Đây là ví dụ khi phân loại vấn đề ổn định.

nhập mô tả hình ảnh ở đây

Đó là lý do tại sao sắp xếp ổn định có ý nghĩa đối với các loại đối tượng, đặc biệt là các loại đối tượng có thể thay đổi và các loại đối tượng có nhiều dữ liệu hơn chỉ là khóa sắp xếp và hợp nhất là một loại như vậy. Nhưng đối với các kiểu nguyên thủy, sự ổn định không chỉ là không thích hợp. Nó vô nghĩa.

Nguồn: INFO


0

Arrays.sortPhương thức của Java sử dụng quicksort, sắp xếp chèn và kết hợp. Thậm chí có cả một nhanh trục xoay đơn và trục kép được triển khai trong mã OpenJDK. Thuật toán sắp xếp nhanh nhất phụ thuộc vào các trường hợp và người chiến thắng là: sắp xếp chèn cho các mảng nhỏ (47 hiện được chọn), sắp xếp hợp nhất cho các mảng được sắp xếp gần hết và sắp xếp nhanh cho các mảng còn lại để Java's Array.sort () cố gắng chọn thuật toán tốt nhất để áp dụng dựa trên các tiêu chí đó.

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.