Hàm sort () của Javascript hoạt động như thế nào?


94

Đoạn mã sau đây sắp xếp mảng này theo thứ tự số như thế nào?

var array=[25, 8, 7, 41]

array.sort(function(a,b){
  return a - b
})

Tôi biết rằng nếu kết quả tính toán là ...

Nhỏ hơn 0 : "a" được sắp xếp để có chỉ số thấp hơn "b".
0: "a" và "b" được coi là bằng nhau và không có sự sắp xếp nào được thực hiện.
Lớn hơn 0: "b" được sắp xếp thành chỉ số thấp hơn "a".

Hàm gọi lại sắp xếp mảng có được gọi nhiều lần trong quá trình sắp xếp không?

Nếu vậy, tôi muốn biết hai số nào được chuyển vào hàm mỗi lần. Tôi cho rằng đầu tiên nó lấy "25" (a) và "8" (b), tiếp theo là "7" (a) và "41" (b), vì vậy:

25 (a) - 8 (b) = 17 (lớn hơn 0, vì vậy hãy sắp xếp "b" để có chỉ số thấp hơn "a"): 8, 25

7 (a) - 41 (b) = -34 (nhỏ hơn 0, vì vậy hãy sắp xếp "a" thành chỉ số thấp hơn "b": 7, 41

Hai bộ số sau đó được sắp xếp như thế nào trong mối quan hệ với nhau?

Hãy giúp một người mới đang gặp khó khăn!


5
Tôi hy vọng điều này có một số ý nghĩa bị cắt xén!
cw84 29/09/09

Câu trả lời:


51

Hàm gọi lại sắp xếp mảng có được gọi nhiều lần trong quá trình sắp xếp không?

Đúng

Nếu vậy, tôi muốn biết hai số nào được chuyển vào hàm mỗi lần

Bạn có thể tìm ra bản thân của mình với:

array.sort((a,b) => {
  console.log(`comparing ${a},${b}`);
  return a > b ? 1
               : a === b ? 0 
                         : -1;
});

BIÊN TẬP

Đây là đầu ra tôi đã có:

25,8
25,7
8,7
25,41

8
thay vì sử dụng console.log để firebug hoặc html DIV của phần tử .innerHTML + = "so sánh" + a + "," + b + "\ n";
Joshua

2
Hãy nhớ rằng đây là một wiki giống như trang web và chúng ta có thể chỉnh sửa người khác câu trả lời để làm cho họ tốt hơn :)
Pablo Fernandez

7
Chỉ cần một lưu ý cho cú pháp ES6 mới: array.sort((a,b) => a - b);là cú pháp hợp lệ
Sterling Archer

1
nếu hàm sắp xếp trả về -ve thì nó hoán đổi và + ve thì nó không hoán đổi ??
Mahi

2
@ShekharReddy bạn vẫn có thể sử dụng các toán tử để so sánh. Tôi đã cập nhật câu trả lời.
OscarRyz

44

Trình thông dịch JavaScript có một số loại triển khai thuật toán sắp xếp được tích hợp sẵn. Nó gọi hàm so sánh một số lần trong quá trình sắp xếp. Số lần hàm so sánh được gọi phụ thuộc vào thuật toán cụ thể, dữ liệu được sắp xếp và thứ tự của nó trước khi sắp xếp.

Một số thuật toán sắp xếp hoạt động kém trên các danh sách đã được sắp xếp vì nó khiến chúng phải so sánh nhiều hơn so với trường hợp điển hình. Những người khác đối phó tốt với danh sách được sắp xếp trước, nhưng có những trường hợp khác, họ có thể bị "lừa" để hoạt động kém.

Có nhiều thuật toán sắp xếp được sử dụng phổ biến vì không có thuật toán nào là hoàn hảo cho mọi mục đích. Hai cách thường được sử dụng nhất để sắp xếp chung là Quicksortmerge sort . Quicksort thường nhanh hơn trong số hai, nhưng sắp xếp hợp nhất có một số thuộc tính tốt có thể làm cho nó trở thành một lựa chọn tổng thể tốt hơn. Sắp xếp hợp nhất là ổn định , trong khi Quicksort thì không. Cả hai thuật toán đều có thể song song hóa, nhưng cách sắp xếp hợp nhất hoạt động làm cho việc triển khai song song hiệu quả hơn, tất cả các thuật toán khác đều bình đẳng.

Trình thông dịch JavaScript cụ thể của bạn có thể sử dụng một trong những thuật toán đó hoặc một cái gì đó khác hoàn toàn. Các ECMAScript tiêu chuẩn không xác định những thuật toán việc thực hiện phù hợp phải sử dụng. Nó thậm chí còn từ chối rõ ràng nhu cầu về sự ổn định.


1
FWIW, Quicksort cơ bản là một trong những thuật toán có thể bị "lừa" để hoạt động kém. Ở dạng đơn giản, nó có hiệu suất O (N ^ 2) đối với các danh sách đã được sắp xếp hoặc đã được đảo ngược chính xác. Hầu hết các thuật toán Quicksort của thư viện có một số tối ưu hóa không rõ ràng giúp tránh các trường hợp xấu nhất thường gặp này.
Adisak 29/09/09

3
JavaScriptCore thực sự sử dụng cây AVL để sắp xếp vì nó cần thiết phải hoạt động một cách xác định khi đối mặt với các hàm so sánh sửa đổi mảng đang được sắp xếp.
olliej 30/09/09

Tiêu chuẩn ECMAScript hiện nay yêu cầu sự ổn định .
ggorlen

11

Các cặp giá trị được so sánh, từng cặp một. Các cặp được so sánh là một chi tiết triển khai - đừng cho rằng chúng sẽ giống nhau trên mọi trình duyệt. Lệnh gọi lại có thể là bất kỳ thứ gì (vì vậy bạn có thể sắp xếp chuỗi hoặc chữ số La Mã hoặc bất kỳ thứ gì khác mà bạn có thể đưa ra một hàm trả về 1,0, -1).

Một điều cần lưu ý với cách sắp xếp của JavaScript là nó không được đảm bảo ổn định.


5

Hàm gọi lại sắp xếp mảng có được gọi nhiều lần trong quá trình sắp xếp không?

Vâng, chính xác là như vậy. Lệnh gọi lại được sử dụng để so sánh các cặp phần tử trong mảng khi cần thiết để xác định thứ tự của chúng. Việc triển khai hàm so sánh đó không điển hình khi xử lý một sắp xếp số. Thông tin chi tiết trong thông số kỹ thuật hoặc trên một số trang web khác dễ đọc hơn .


4

Hàm gọi lại sắp xếp mảng có được gọi nhiều lần trong quá trình sắp xếp không?

Vì đây là một sắp xếp so sánh, cho N mục, hàm gọi lại sẽ được gọi trung bình (N * Lg N) lần cho một loại nhanh như Quicksort . Nếu thuật toán được sử dụng giống như Bubble Sort , thì hàm gọi lại sẽ được gọi trung bình (N * N) lần.

Số lượng lệnh gọi tối thiểu cho một sắp xếp so sánh là (N-1) và điều đó chỉ để phát hiện một danh sách đã được sắp xếp (tức là xuất hiện sớm trong Sắp xếp bong bóng nếu không có hoán đổi nào xảy ra).


3

Kiến thức sâu sắc

Nếu kết quả là âm, a được sắp xếp trước b.

Nếu kết quả là dương b được sắp xếp trước a.

Nếu kết quả là 0 thì không có thay đổi nào được thực hiện với thứ tự sắp xếp của hai giá trị.

GHI CHÚ:

Mã này là chế độ xem bên trong của phương pháp sắp xếp từng bước.

ĐẦU RA:

let arr = [90, 1, 20, 14, 3, 55];
var sortRes = [];
var copy = arr.slice();		//create duplicate array
var inc = 0;	//inc meant increment
copy.sort((a, b) => {
	sortRes[inc] = [ a, b, a-b ];
	inc += 1;
	return a - b;
});
var p = 0;
for (var i = 0; i < inc; i++) {
	copy = arr.slice();
	copy.sort((a, b) => {
		p += 1;
		if (p <= i ) {
			return a - b;
		}
		else{
			return false;
		}
	});
	p = 0;
	console.log(copy +' \t a: '+ sortRes[i][0] +' \tb: '+ sortRes[i][1] +'\tTotal: '+ sortRes[i][2]);
}


0

chạy mã này. Bạn có thể xem quy trình phân loại chính xác từng bước từ đầu đến cuối.

var array=[25, 8, 7, 41]
var count = 1;
array.sort( (a,b) => { 
console.log(`${count++}). a: ${a} | b: ${b}`);
return a-b;
});
console.log(array);

đã sao chép và dán vào bảng điều khiển và nó chỉ trả về không xác định.
iPzard

0

Để giúp làm rõ hành vi của Array#sortvà bộ so sánh của nó, hãy xem xét loại chèn ngây thơ này được dạy trong các khóa học lập trình bắt đầu:

const sort = arr => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && arr[j-1] > arr[j]; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array);
console.log("" + array);

Bỏ qua lựa chọn sắp xếp chèn như các thuật toán, tập trung vào các mã hóa cứng so sánh: arr[j-1] > arr[j]. Điều này có hai vấn đề liên quan đến cuộc thảo luận:

  1. Các >nhà điều hành được gọi vào cặp phần tử mảng nhưng nhiều điều bạn có thể muốn sắp xếp như các đối tượng không đáp ứng với >một cách hợp lý (cùng sẽ là sự thật nếu chúng ta sử dụng -).
  2. Ngay cả khi bạn đang làm việc với các con số, đôi khi bạn muốn một số sắp xếp khác hơn là sắp xếp tăng dần đã được đưa ra ở đây.

Chúng tôi có thể khắc phục những sự cố này bằng cách thêm một comparefnđối số mà bạn đã quen thuộc:

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array, (a, b) => a - b);
console.log("" + array);

sort(array, (a, b) => b - a);
console.log("" + array);

const objArray = [{id: "c"}, {id: "a"}, {id: "d"}, {id: "b"}];
sort(objArray, (a, b) => a.id.localeCompare(b.id));
console.log(JSON.stringify(objArray, null, 2));

Giờ đây, thói quen sắp xếp ngây thơ đã được khái quát hóa. Bạn có thể biết chính xác khi nào gọi lại này được gọi, trả lời nhóm mối quan tâm đầu tiên của bạn:

Hàm gọi lại sắp xếp mảng có được gọi nhiều lần trong quá trình sắp xếp không? Nếu vậy, tôi muốn biết hai số nào được chuyển vào hàm mỗi lần

Chạy đoạn mã dưới đây cho thấy rằng, có, hàm được gọi nhiều lần và bạn có thể sử dụng console.logđể xem những số nào đã được chuyển vào:

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

console.log("on our version:");
const array = [3, 0, 4, 5];
sort(array, (a, b) => console.log(a, b) || (a - b));
console.log("" + array);

console.log("on the builtin:");
console.log("" + 
  [3, 0, 4, 5].sort((a, b) => console.log(a, b) || (a - b))
);

Bạn hỏi:

Hai bộ số sau đó được sắp xếp như thế nào trong mối quan hệ với nhau?

Nói chính xác với thuật ngữ, abkhông phải là tập hợp số - chúng là các đối tượng trong mảng (trong ví dụ của bạn, chúng là số).

Sự thật là, chúng được sắp xếp như thế nào không quan trọng vì nó phụ thuộc vào việc triển khai. Nếu tôi đã sử dụng một thuật toán sắp xếp khác với sắp xếp chèn, bộ so sánh có thể sẽ được gọi trên các cặp số khác nhau, nhưng ở cuối lệnh gọi sắp xếp, bất biến quan trọng đối với lập trình viên JS là mảng kết quả được sắp xếp theo bộ so sánh, giả sử bộ so sánh trả về các giá trị tuân theo hợp đồng bạn đã nêu (<0 khi a < b, 0 khi a === bvà> 0 khi a > b).

Cũng theo nghĩa là tôi có quyền tự do thay đổi cách triển khai sắp xếp của mình miễn là tôi không vi phạm đặc điểm kỹ thuật của mình, việc triển khai ECMAScript có thể tự do lựa chọn cách triển khai sắp xếp trong giới hạn của đặc tả ngôn ngữ , do đó Array#sortcó thể sẽ tạo ra các lệnh gọi so sánh khác nhau trên các động cơ khác nhau. Người ta sẽ không viết mã trong đó logic dựa vào một số chuỗi so sánh cụ thể (và ngay từ đầu trình so sánh cũng không nên tạo ra các tác dụng phụ).

Ví dụ: động cơ V8 (tại thời điểm viết bài) gọi Timsort khi mảng lớn hơn một số phần tử được tính toán trước và sử dụng sắp xếp chèn nhị phân cho các phần mảng nhỏ. Tuy nhiên, nó đã từng sử dụng quicksort không ổn định và có thể sẽ đưa ra một chuỗi đối số và lệnh gọi khác nhau tới trình so sánh.

Vì các triển khai sắp xếp khác nhau sử dụng giá trị trả về của hàm so sánh khác nhau, điều này có thể dẫn đến hành vi đáng ngạc nhiên khi trình so sánh không tuân thủ hợp đồng. Xem chủ đề này để biết ví dụ.


-2
var array=[25, 8, 7, 41]

array.sort(function(a,b){
  console.log(`a = ${a} , b = ${b}`);
  return a - b
});

ĐẦU RA

  • a = 8, b = 25
  • a = 7, b = 8
  • a = 41, b = 7
  • a = 41, b = 8
  • a = 41, b = 25

trong Trình duyệt của tôi (Phiên bản Google Chrome 70.0.3538.77 (Bản dựng chính thức) (64-bit)) trong lần lặp đầu tiên, đối số a là phần tử Thứ hai trong một mảng và đối số b là phần tử đầu tiên của một mảng.

nếu hàm So sánh trả về

  1. Giá trị âm hơn b được chuyển tới a.
  2. Giá trị dương hơn a được chuyển tới b.
  3. 0 (Không) cả a & b sẽ vẫn như cũ.
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.