Як працює сортування () Javascript?


94

Як наступний код сортує цей масив у порядку чисел?

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

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

Я знаю, що якщо результат обчислення буде ...

Менше 0 : "a" сортується, щоб бути нижчим індексом, ніж "b".
Нуль: "a" і "b" вважаються рівними, і сортування не проводиться.
Більше ніж 0: "b" сортується, щоб бути нижчим індексом, ніж "a".

Чи викликається функція зворотного виклику масиву багато разів протягом сортування?

Якщо так, я хотів би знати, які два числа передаються у функцію кожного разу. Я припустив, що спочатку взяли "25" (а) і "8" (б), а потім "7" (а) і "41" (б), так:

25 (a) - 8 (b) = 17 (більше нуля, тому сортуйте "b", щоб бути нижчим показником, ніж "a"): 8, 25

7 (a) - 41 (b) = -34 (менше нуля, тому сортуйте "a", щоб бути нижчим індексом, ніж "b": 7, 41

Як потім сортуються два набори чисел по відношенню один до одного?

Будь ласка, допоможіть новачку, що бореться!


5
Сподіваюся, це має якийсь спотворений сенс!
cw84,

Відповіді:


51

Чи викликається функція зворотного виклику масиву багато разів протягом сортування?

Так

Якщо так, я хотів би знати, які два числа передаються у функцію кожного разу

Ви можете дізнатись про себе за допомогою:

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

РЕДАГУВАТИ

Це результат, який я отримав:

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

8
скоріше зробіть console.log для файлу firebug або html DIV .innerHTML + = "порівняння" + a + "," + b + "\ n";
Джошуа

2
Пам’ятайте, що це сайт, схожий на вікі, і ми можемо редагувати відповіді інших, щоб покращити їх :)
Пабло Фернандес

7
Лише примітка для нового синтаксису ES6: array.sort((a,b) => a - b);дійсний синтаксис
Sterling Archer

1
якщо функція сортування повертає -ve, то вона міняє місцями і + ve, тоді вона не замінює ??
Mahi

2
@ShekharReddy, ти все ще можеш використовувати оператори для порівняння. Я оновив відповідь.
OscarRyz

44

Інтерпретатор JavaScript має вбудовану якусь реалізацію алгоритму сортування . Він викликає функцію порівняння деяку кількість разів під час операції сортування. Кількість викликів функції порівняння залежить від конкретного алгоритму, даних, які потрібно сортувати, та порядку, в якому вона знаходиться перед сортуванням.

Деякі алгоритми сортування погано працюють у вже відсортованих списках, оскільки це змушує їх проводити набагато більше порівнянь, ніж у типовому випадку. Інші добре справляються із заздалегідь відсортованими списками, але є й інші випадки, коли їх можна "обманути" погано.

Існує багато загальновживаних алгоритмів сортування, оскільки жоден алгоритм не є ідеальним для всіх цілей. Два найчастіше використовувані для загального сортування - це швидке сортування та злиття . Quicksort часто є швидшим з двох, але сортування злиття має кілька приємних властивостей, які можуть зробити його кращим загальним вибором. Сортування злиття стабільне , а Quicksort - ні. Обидва алгоритми можна розпаралелювати, але те, як працює злиття, робить паралельну реалізацію більш ефективною, за інших рівних умов.

Ваш конкретний інтерпретатор JavaScript може використовувати один із цих алгоритмів або щось інше цілком. ECMAScript стандарт не визначає , який алгоритм повної реалізація повинна використовувати. Це навіть явно заперечує необхідність стабільності.


1
FWIW, базовий Quicksort - це один з алгоритмів, який може бути "обманутий", щоб погано працювати. У простій формі він має ефективність O (N ^ 2) для списків, які вже відсортовані або точно відмінені. Більшість алгоритмів Quicksort бібліотеки мають ряд неочевидних оптимізацій, які допомагають уникнути цих найпоширеніших найгірших сценаріїв.
Adisak

3
JavaScriptCore насправді використовує дерево AVL для сортування, оскільки необхідно поводитися детерміновано перед функціями порівняння, які змінюють сортуваний масив.
olliej


11

Пари значень порівнюються по одній парі за раз. Порівняні пари - це деталь реалізації - не припускайте, що вони будуть однаковими в кожному браузері. Зворотний дзвінок може бути будь-яким (так що ви можете сортувати рядки або римські цифри або що-небудь ще, де ви можете придумати функцію, яка повертає 1,0, -1).

З сортуванням JavaScript слід пам’ятати одне: це не гарантує стабільність роботи.


5

Чи викликається функція зворотного виклику масиву багато разів протягом сортування?

Так, саме це. Зворотний виклик використовується для порівняння пар елементів у масиві за необхідності, щоб визначити, в якому порядку вони повинні бути. Ця реалізація функції порівняння є нетиповою при роботі з числовим сортуванням. Деталі в специфікації або на деяких інших більш читабельних сайтах.


4

Чи викликається функція зворотного виклику масиву багато разів протягом сортування?

Оскільки це сортування порівняння, враховуючи N елементів, функцію зворотного виклику слід викликати в середньому (N * Lg N) разів для швидкого сортування, як Quicksort . Якщо використовуваний алгоритм подібний до Bubble Sort , тоді функція зворотного виклику буде викликана в середньому (N * N) разів.

Мінімальна кількість викликів для сортування порівняння - (N-1), і це лише для виявлення вже відсортованого списку (тобто на початку в Bubble Sort, якщо не відбувається заміни).


3

Глибоко знання

Якщо результат негативний, a сортується перед b.

Якщо результат позитивний, b сортується перед a.

Якщо результат 0, ніяких змін із порядком сортування двох значень не проводиться.

ПРИМІТКА:

Цей код - це вид усередині методу сортування поетапно.

ВИХІД:

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

запустити цей код. Ви можете бачити точний процес поетапного сортування від початку до кінця.

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);

скопійовано та вставлено в консоль, і вона просто повернулася невизначеною.
iPzard

0

Щоб допомогти прояснити поведінку Array#sortта його компаратор, розглянемо цей наївний сорт вставки, який викладали на початкових курсах програмування:

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);

Чи не звертаючи уваги на вибір вставки роду в якості алгоритму, фокусу на закодованому компараторе: arr[j-1] > arr[j]. Це має дві проблеми, що стосуються дискусії:

  1. >Оператор викликається на пари елементів масиву , але багато речей , які ви , можливо , захочете сортувати такі як об'єкти не відповідають >в розумних межах (те ж саме було б правильно , якби ми використовували -).
  2. Навіть якщо ви працюєте з числами, часто ви хочете отримати інше розташування, крім зростаючого сорту, який тут запечений.

Ми можемо виправити ці проблеми, додавши comparefnаргумент, який вам знайомий:

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));

Тепер узагальнена процедура наївного сортування. Ви можете точно побачити, коли викликається цей зворотний виклик, відповідаючи на свої перші проблеми:

Чи викликається функція зворотного виклику масиву багато разів протягом сортування? Якщо так, я хотів би знати, які два числа передаються у функцію кожного разу

Запуск коду нижче показує, що так, функція викликається багато разів, і ви можете використовувати, console.logщоб побачити, які числа були передані:

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))
);

Ви запитаєте:

Як потім сортуються два набори чисел по відношенню один до одного?

Точніше з термінологією, aа bне набори чисел - це об’єкти в масиві (у вашому прикладі це числа).

Правда в тому, що не має значення, як вони сортуються, оскільки це залежить від реалізації. Якби я використовував інший алгоритм сортування, ніж вставка сортування, компаратор, мабуть, буде викликаний на різних парах чисел, але в кінці виклику сортування інваріант, який має значення для програміста JS, полягає в тому, що масив результатів сортується відповідно до comparator, припускаючи, що компаратор повертає значення, що відповідають заявленому вами договору (<0, колиa < b , 0 коли a === bі> 0 коли a > b).

У тому самому розумінні, що я маю свободу змінювати реалізацію мого сортування, доки я не порушую свою специфікацію, реалізації ECMAScript можуть вільно вибирати реалізацію сортування в межах специфікації мови. , тому Array#sort, ймовірно, будуть вироблятися різні виклики порівняння на різних двигунах. Не можна писати код там, де логіка покладається на певну послідовність порівнянь (і компаратор не повинен спочатку викликати побічні ефекти).

Наприклад, движок V8 (на момент написання) викликає Timsort, коли масив перевищує деяку попередньо обчислену кількість елементів, і використовує двійкове сортування вставки для невеликих фрагментів масиву. Однак раніше він використовував швидку сорту, яка є нестабільною і, швидше за все, даватиме іншу послідовність аргументів та викликів до компаратора.

Оскільки різні реалізації сортування по-різному використовують повернене значення функції порівняння, це може призвести до дивовижної поведінки, коли компаратор не дотримується договору. Див. Цей приклад для прикладу.


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

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

ВИХІД

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

у моєму браузері (Google Chrome Версія 70.0.3538.77 (Офіційна збірка) (64-розрядна версія)) у першій ітерації аргумент a є другим елементом масиву, а аргумент b - першим елементом масиву.

якщо повертається функція порівняння

  1. Негативне значення, ніж b, переміщується вперед до a.
  2. Позитивне значення, ніж a, переміщується вперед до b.
  3. 0 (Нуль) і a & b залишаться такими, як вони є.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.