概述
由一道排序算法题引起的思考。
一开始在找关于sort排序方法是怎么用的,过程中发现的一些关于sort的知识:
语法
arrayObject.sort(sortby)
说明
如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。
如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数
a 和 b,其返回值如下: 若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。 若 a 等于
b,则返回 0。 若 a 大于 b,则返回一个大于 0 的值。
所以说,sort() 方法用于对数组的元素进行排序,并返回数组。默认排序顺序是根据字符串Unicode码点。如果对数字从小到大或者从大到小排序,要实现这一点,就必须使用一个排序函数。
下面是我从网上找到的一个例子
<script type="text/javascript">
function sortNumber(a,b)
{
return a - b
}
var arr = new Array(6)
arr[0] = "10"
arr[1] = "5"
arr[2] = "40"
arr[3] = "25"
arr[4] = "1000"
arr[5] = "1"
document.write(arr + "<br />")
document.write(arr.sort(sortNumber))
</script>
输出
10,5,40,25,1000,1
1,5,10,25,40,1000
这里我发现了一个问题,sort是怎么把数组进行排序的,sortNumber这个函数里面,a和b到底是代表着什么,是每一个相邻的数组元素还是什么,我陷入了沉思……
另外还需要注意的一点是,sort方法会改变引用数组,如果想要原数组需要提前复制出来一份。
在网上查阅了很多资料,发现也有小伙伴有我这种疑惑,但是大神解答的我都没有太明白,所以还是自己去看源码吧,多的不说了,直接上。
Chrome V8引擎的源码
function InnerArraySort(array, length, comparefn) {
// In-place QuickSort algorithm.
// For short (length <= 22) arrays, insertion sort is used for efficiency.
if (!IS_CALLABLE(comparefn)) {
comparefn = function (x, y) {
if (x === y) return 0;
if (%_IsSmi(x) && %_IsSmi(y)) {
return %SmiLexicographicCompare(x, y);
}
x = TO_STRING(x);
y = TO_STRING(y);
if (x == y) return 0;
else return x < y ? -1 : 1;
};
}
var InsertionSort = function InsertionSort(a, from, to) {
for (var i = from + 1; i < to; i++) {
var element = a[i];
for (var j = i - 1; j >= from; j--) {
var tmp = a[j];
var order = comparefn(tmp, element);
if (order > 0) {
a[j + 1] = tmp;
} else {
break;
}
}
a[j + 1] = element;
}
};
var GetThirdIndex = function(a, from, to) {
var t_array = new InternalArray();
// Use both 'from' and 'to' to determine the pivot candidates.
var increment = 200 + ((to - from) & 15);
var j = 0;
from += 1;
to -= 1;
for (var i = from; i < to; i += increment) {
t_array[j] = [i, a[i]];
j++;
}
t_array.sort(function(a, b) {
return comparefn(a[1], b[1]);
});
var third_index = t_array[t_array.length >> 1][0];
return third_index;
}
var QuickSort = function QuickSort(a, from, to) {
var third_index = 0;
while (true) {
// Insertion sort is faster for short arrays.
if (to - from <= 10) {
InsertionSort(a, from, to);
return;
}
if (to - from > 1000) {
third_index = GetThirdIndex(a, from, to);
} else {
third_index = from + ((to - from) >> 1);
}
// Find a pivot as the median of first, last and middle element.
var v0 = a[from];
var v1 = a[to - 1];
var v2 = a[third_index];
var c01 = comparefn(v0, v1);
if (c01 > 0) {
// v1 < v0, so swap them.
var tmp = v0;
v0 = v1;
v1 = tmp;
} // v0 <= v1.
var c02 = comparefn(v0, v2);
if (c02 >= 0) {
// v2 <= v0 <= v1.
var tmp = v0;
v0 = v2;
v2 = v1;
v1 = tmp;
} else {
// v0 <= v1 && v0 < v2
var c12 = comparefn(v1, v2);
if (c12 > 0) {
// v0 <= v2 < v1
var tmp = v1;
v1 = v2;
v2 = tmp;
}
}
// v0 <= v1 <= v2
a[from] = v0;
a[to - 1] = v2;
var pivot = v1;
var low_end = from + 1;
// Upper bound of elements lower than pivot.
var high_start = to - 1;
// Lower bound of elements greater than pivot.
a[third_index] = a[low_end];
a[low_end] = pivot;
// From low_end to i are elements equal to pivot.
// From i to high_start are elements that haven't been compared yet.
partition: for (var i = low_end + 1; i < high_start; i++) {
var element = a[i];
var order = comparefn(element, pivot);
if (order < 0) {
a[i] = a[low_end];
a[low_end] = element;
low_end++;
} else if (order > 0) {
do {
high_start--;
if (high_start == i) break partition;
var top_elem = a[high_start];
order = comparefn(top_elem, pivot);
} while (order > 0);
a[i] = a[high_start];
a[high_start] = element;
if (order < 0) {
element = a[i];
a[i] = a[low_end];
a[low_end] = element;
low_end++;
}
}
}
if (to - high_start < low_end - from) {
QuickSort(a, high_start, to);
to = low_end;
} else {
QuickSort(a, from, low_end);
from = high_start;
}
}
};
// Copy elements in the range 0..length from obj's prototype chain
// to obj itself, if obj has holes. Return one more than the maximal index
// of a prototype property.
var CopyFromPrototype = function CopyFromPrototype(obj, length) {
var max = 0;
for (var proto = %object_get_prototype_of(obj); proto;
proto = %object_get_prototype_of(proto)) {
var indices = IS_PROXY(proto) ? length : %GetArrayKeys(proto, length);
if (IS_NUMBER(indices)) {
// It's an interval.
var proto_length = indices;
for (var i = 0; i < proto_length; i++) {
if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) {
obj[i] = proto[i];
if (i >= max) { max = i + 1; }
}
}
} else {
for (var i = 0; i < indices.length; i++) {
var index = indices[i];
if (!HAS_OWN_PROPERTY(obj, index) && HAS_OWN_PROPERTY(proto, index)) {
obj[index] = proto[index];
if (index >= max) { max = index + 1; }
}
}
}
}
return max;
};
看完源码发现,V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,数组长度小于等于 22 的用插入排序 InsertionSort,比22大的数组则使用快速排序 QuickSort。源码中注释这样写道:
// In-place QuickSort algorithm.
// For short (length <= 22) arrays, insertion sort is used for efficiency.
但是代码里写的却是,让我很懵
if (to - from <= 10) {
InsertionSort(a, from, to);
return;
}
那我们就自己来证实一下:
- 当排序数组元素不大于10个时:
var s = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
s.sort(function(a, b) {
if(a == b) {
console.log(a + "=" + b);
return 0;
}
if(a < b) {
console.log(a + "<" + b);
return -1;
}
if(a > b) {
console.log(a + ">" + b);
return 1;
}
});
console.log(s);
结果:
10>9
10>8
9>8
10>7
9>7
8>7
10>6
9>6
8>6
7>6
10>5
9>5
8>5
7>5
6>5
10>4
9>4
8>4
7>4
6>4
5>4
10>3
9>3
8>3
7>3
6>3
5>3
4>3
10>2
9>2
8>2
7>2
6>2
5>2
4>2
3>2
10>1
9>1
8>1
7>1
6>1
5>1
4>1
3>1
2>1
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
可见,其排序方式为典型的插入排序。
- 当排序数组元素为11个(或11个以上)时:
var s = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
s.sort(function(a, b) {
if(a == b) {
console.log(a + "=" + b);
return 0;
}
if(a < b) {
console.log(a + "<" + b);
return -1;
}
if(a > b) {
console.log(a + ">" + b);
return 1;
}
});
console.log(s);
结果:
11>1
1<6
11>6
9>6
2<6
8>6
3<6
7>6
4<6
10>6
5<6
1<2
2<3
3<4
4<5
10>7
10>8
7<8
10>9
8<9
10<11
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
可见,其排序方式为快速排序。
总结:
1.a和b的取值是先通过数组长度筛选排序方式,然后通过from和to进行数组排序。
2.排序的方法选择可能是源码里注释写错了。
补充:
Mozilla/Firefox : 归并排序(jsarray.c 源码)
Webkit :底层实现用了 C++ 库中的 qsort() 方法(JSArray.cpp 源码)
———————————————————————————————————————————————
本文仅供个人学习使用,若有错误之处,请指正,必定尽快更改。
最后
以上就是谦让唇膏为你收集整理的关于js数组sort方法的实现原理总结的全部内容,希望文章能够帮你解决关于js数组sort方法的实现原理总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复