Javascript sort()如何工作?

时间:2009-09-29 20:21:06

标签: javascript sorting comparator

以下代码如何将此数组按数字顺序排序?

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

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

我知道如果计算结果是......

小于0 :“a”被排序为比“b”更低的索引。
零:“a”和“b”被认为是相等的,并且不执行排序 大于0:“b”被排序为比“a”更低的索引。

在排序过程中是否多次调用数组排序回调函数?

如果是这样,我想知道每次将哪两个数字传递给函数。我假设它首先取“25”(a)和“8”(b),然后是“7”(a)和“41”(b),所以:

25(a) - 8(b)= 17(大于零,所以将“b”排序为低于“a”的索引):8,25

7(a) - 41(b)= -34(小于零,所以将“a”排序为比“b”更低的索引:7,41

这两组数字如何相互排序?

请帮助一个挣扎的新手!

9 个答案:

答案 0 :(得分:40)

  

在排序过程中多次调用数组排序回调函数?

  

如果是这样,我想知道每次将哪两个数字传递给函数

你可以通过以下方式找到自己:

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

答案 1 :(得分:37)

JavaScript解释器内置了某种sort algorithm实现。它在排序操作期间多次调用比较函数。调用比较函数的次数取决于特定算法,要排序的数据以及排序之前的顺序。

某些排序算法在已经排序的列表上表现不佳,因为它导致它们比典型情况下进行更多的比较。其他人可以很好地处理预先排序的列表,但还有其他情况可能被“欺骗”到表现不佳的情况。

有许多常用的排序算法,因为没有一种算法适用于所有目的。最常用于通用排序的两个是Quicksortmerge sort。 Quicksort通常是两者中较快的,但合并排序有一些很好的属性,可以使它成为更好的整体选择。合并排序为stable,而Quicksort则不是。这两种算法都是可并行化的,但合并排序的工作方式使并行实现更有效,其他条件相同。

您的特定JavaScript解释器可能会完全使用其中一种算法或其他算法。必须使用符合条件的ECMAScript标准does not specify which algorithm。它甚至明确否定了稳定的必要性。

答案 2 :(得分:10)

比较一对值,一次一对。被比较的对是一个实现细节 - 不要以为它们在每个浏览器上都是相同的。回调可以是任何内容(因此您可以对字符串或罗马数字或其他任何可以提供返回1,0,-1的函数进行排序)。

要记住JavaScript的一点,那就是它不能保证稳定。

答案 3 :(得分:5)

在排序过程中多次调用数组排序回调函数?

是的,就是这样。回调用于比较数组中的元素对,以确定它们应该处于什么顺序。在处理数字排序时,比较函数的实现不是非典型的。 the specsome个其他more readable网站上的详细信息。

答案 4 :(得分:4)

  

在排序过程中是否多次调用数组排序回调函数?

由于这是一个比较排序,给定N个项目,对于像Quicksort这样的快速排序,应该平均调用回调函数(N * Lg N)次。如果使用的算法类似Bubble Sort,则回调函数将平均调用(N * N)次。

比较排序的最小调用次数为(N-1),并且仅用于检测已排序的列表(即,如果没有发生交换,则在冒泡排序中提前)。

答案 5 :(得分:3)

深入了解

如果结果为负,则a排在b之前。

如果结果为正,则b排在a之前。

如果结果为0,则不会更改两个值的排序顺序。

注意:

此代码是 sort 方法 逐步中的视图。

输出:

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

答案 6 :(得分:1)

为帮助阐明Array#sort及其比较器的行为,请考虑在开始编程课程中所学的朴素insertion 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);

忽略选择插入排序作为算法,将重点放在hardcoded比较器: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))
);

您问:

然后如何将两组数字彼此相对排序?

确切地说,ab不是数字的集合,它们是数组中的对象(在您的示例中,它们是数字)。

事实是,如何排序并不重要,因为它们取决于实现。如果我使用与插入排序不同的排序算法,则比较器可能会在不同的数字对上被调用,但是在排序调用结束时,对JS程序员而言重要的不变式是结果数组根据比较器,假设比较器返回的值符合您指定的合同(a < b时为<0,a === b时为0,a > b时为> 0)。

从某种意义上说,只要我不违反规范,我就可以自由更改排序的实现,ECMAScript的实现可以在language specification的范围内自由选择排序实现,因此Array#sort可能会在不同引擎上产生不同的比较器调用。如果逻辑依赖于某些特定的比较序列,则不会编写代码(比较器也不会首先产生副作用)。

例如,当数组大于某些预先计算的元素数时,V8引擎(在编写本文时)将调用Timsort,并将binary insertion sort用于较小的数组块。但是,它曾经使用quicksort,它很不稳定,可能会给比较器提供不同的参数和调用顺序。

由于不同的排序实现以不同的方式使用比较器函数的返回值,因此当比较器不遵守合同时,这可能导致令人惊讶的行为。有关示例,请参见this thread

答案 7 :(得分: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);

答案 8 :(得分:-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. a和b均保持为0(零)。