table.sort 概述
- table.sort函数的本质是快排算法,在此基础上做了三点优化
- 每次分割数组的锚点是从当前分割数组的初始点、中间点、结尾点中选择中位数作为锚点,减少极端情况下快排的次数每次分割数组的锚点是从当前分割数组的初始点、中间点、结尾点中选择中位数作为锚点,减少极端情况下快排的次数
- 对于迭代函数中的分割数组长度小于等于3的,直接通过比较大小交换位置,形成有序数组,这样做的目的是减少递归调用的深度
- 每次通过锚点把分割数组分成两半之后,对长度较小的一半进行递归调用,另一半则继续通过While继续分割处理,目的应该也是减少递归调用的深度
table.sort源码及注释
static void auxsort (lua_State *L, int l, int u) {
while (l < u) { /* for tail recursion */
int i, j;
/* sort elements a[l], a[(l+u)/2] and a[u] */
lua_rawgeti(L, 1, l);
lua_rawgeti(L, 1, u);
if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
set2(L, l, u); /* swap a[l] - a[u] */
else
lua_pop(L, 2);
if (u-l == 1) break; /* only 2 elements */
i = (l+u)/2;
lua_rawgeti(L, 1, i);
lua_rawgeti(L, 1, l);
if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */
set2(L, i, l);
else {
lua_pop(L, 1); /* remove a[l] */
lua_rawgeti(L, 1, u);
if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */
set2(L, i, u);
else
lua_pop(L, 2);
}
if (u-l == 2) break; /* only 3 elements */
lua_rawgeti(L, 1, i); /* Pivot */
lua_pushvalue(L, -1);
lua_rawgeti(L, 1, u-1);
set2(L, i, u-1);
//上面代码是对分割数组长度小于等于3的进行比较和排序
//并从数组初始点、中间点、结尾点中选择中位数作为锚点
/* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
i = l; j = u-1;
for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
/* repeat ++i until a[i] >= P */
while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
if (i>u) luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[i] */
}
/* repeat --j until a[j] <= P */
while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
if (j<l) luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[j] */
}
if (j<i) {
lua_pop(L, 3); /* pop pivot, a[i], a[j] */
break;
}
set2(L, i, j);
}
lua_rawgeti(L, 1, u-1);
lua_rawgeti(L, 1, i);
set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */
//上面代码是快排算法,依据选择的锚点把小于锚点的放在一边,大于锚点的放在另一边
/* a[l..i-1] <= a[i] == P <= a[i+1..u] */
/* adjust so that smaller half is in [j..i] and larger one in [l..u] */
if (i-l < u-i) {
j=l; i=i-1; l=i+2;
}
else {
j=i+1; i=u; u=j-2;
}
auxsort(L, j, i); /* call recursively the smaller one */
} /* repeat the routine for the larger one */
//上面代码是让分割后长度较短的数组继续迭代,长度较长的则继续通过while进行快排算法,减少递归调用的次数
}
invalid order function for sorting报错的原因及解决方法
while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
if (i>u) luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[i] */
}
/* repeat --j until a[j] <= P */
while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
if (j<l) luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[j] */
}
在源码中可以看到报错的位置。当锚点的值与边界值相等,这时如果排序方法sort_comp返回true,则会造成数组越界,而解决方案也很简单——当比较的两个值相等时,返回false即可。
个人认为较好的lua排序函数写法
假设战力排行榜有战力,等级两个排序因子,要求先按战力从高往低排,再按等级从高往低排,那么排序函数可以这么写。
table.sort(战力排行榜, function(a, b)
if a.战力 ~= b.战力 then
return a.战力 > b.战力
end
if a.等级 ~= b.等级 then
return a.等级 > b.等级
end
return false
end)
ps:上文提及lua版本是 5.1