117

以下代码如何按数字顺序对该数组进行排序?

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

那么这两组数字是如何相互排序的呢?

请帮助一个苦苦挣扎的新手!

4

7 回答 7

58

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

是的

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

您可以通过以下方式了解自己:

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
于 2009-09-29T20:27:34.753 回答
48

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

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

有许多常用的排序算法,因为没有一种算法可以完美地用于所有目的。最常用于通用排序的两个是快速排序归并排序。快速排序通常是两者中较快的,但归并排序有一些不错的属性,可以使其成为更好的整体选择。归并排序是稳定的,而快速排序不是。两种算法都是可并行化的,但归并排序的工作方式使并行实现更加高效,其他条件相同。

您的特定 JavaScript 解释器可能会使用其中一种算法或完全使用其他算法。ECMAScript标准没有指定符合标准的实现必须使用哪种算法。它甚至明确否认需要稳定。

于 2009-09-29T20:27:05.373 回答
13

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

JavaScript 的排序要记住的一件事是它不能保证是稳定的。

于 2009-09-29T20:32:47.713 回答
6

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

是的,就是这样。回调用于根据需要比较数组中的元素对,以确定它们的顺序。在处理数字排序时,比较函数的实现不是典型的。规范或其他一些更易读的网站上的详细信息。

于 2009-09-29T20:26:42.940 回答
6

深知

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

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

如果结果为 0,则不会对这两个值的排序顺序进行任何更改。

笔记:

这段代码是sort方法里面的view一步一步的。

输出:

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

于 2020-02-20T06:05:05.657 回答
5

为了帮助阐明其行为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 程序员重要的不变量是结果数组是根据排序的比较器,假设比较器返回符合您声明的合同的值(< 0 when a < b, 0 whena === b和 > 0 when a > b)。

同样,只要我不违反规范,我就可以自由更改排序的实现,ECMAScript 的实现可以在语言规范的范围内自由选择排序实现,因此Array#sort可能会产生不同的比较器调用在不同的引擎上。一个人不会编写逻辑依赖于某些特定比较序列的代码(比较器也不应该首先产生副作用)。

例如,V8 引擎(在撰写本文时)在数组大于某个预先计算的元素数量时调用Timsort ,并对小数组块使用二进制插入排序。但是,它曾经使用不稳定的快速排序,并且可能会给出不同的参数序列和对比较器的调用。

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

于 2020-10-06T22:58:30.707 回答
4

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

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

比较排序的最小调用次数是 (N-1),这只是为了检测已经排序的列表(即,如果没有发生交换,则在冒泡排序中提前退出)。

于 2009-09-29T20:35:47.727 回答