在一次采访中,我被要求编写一个程序/算法以使用递归对数字数组进行排序。
尽管我隐约回答了它,但我尝试并提出了以下代码:
您可以使用以下JSFiddle链接进行播放。
function sort(arr) {
if (arr.length === 2) {
const v1 = arr[0];
const v2 = arr[1];
const isGreater = (
(isString(v1) && isString(v2) && v1.toString().toLocaleCompare(v2) > 0) ||
(isNumber(v1) && isNumber(v2) && v1 > v2)
);
return isGreater ? [ v2, v1 ] : [ v1, v2 ];
} else {
const last = arr.pop();
const ret = sort(arr);
const newLast = ret.peekLast();
if (newLast < last) {
return [ ...ret, last ];
} else {
return sort( [ last, ...ret ] );
}
}
}
function isString(value) { return typeof value === 'string'; }
function isNumber(value) { return Number.isFinite(value); }
Array.prototype.peekLast = function () { return this.slice().pop(); }
//console.log(sort([1,2,3,4,5]))
console.log(sort([5,4,3,2,1]))
我实施的算法是:
newLast
的值大于previousLast
previousLast
作为第一个元素,然后再次使用此数组进行调用。previousLast
进行数组并返回它。 问题是,是否有更好的方法来实现(算法明智的)?
注意:我不希望代码有所改进。这个问题的目的是改善算法部分或我错过的任何常规内容。
我也知道,当前代码不支持:
谢谢!
答案 0 :(得分:2)
我认为,大多数访问者都希望您针对此问题回答quicksort或merge sort(或同时回答两者)。在这两种快速排序中,由于合并排序的合并步骤很容易弄乱,因此更容易记住和重新创建。
Quicksort是一种非常漂亮的算法,非常适合javascript的功能工具。如果您要进行采访,那真的很值得理解:
const arr = [6, 1, 5, 3, 9, 6, 7, 10, 16, 4, 0, 12, 2]
function qsort(arr){
if (arr.length < 2) return arr
// choose a pivot, p
// the choice of pivot can effect worst-case performance
// for this, we'll just use the first element.
const [p, ...rest] = arr
// partition array into element greater and lesser that the pivot
// this can be optimized so you don't loop through the array twice
const low = rest.filter(n => n <= p)
const high = rest.filter(n => n > p)
// recurse on both partitions and reassemble as recursion unwinds
return [...qsort(low), p, ...qsort(high)]
}
console.log(qsort(arr).join(', '))
答案 1 :(得分:1)
如果由于这一行而有重复的元素,则您的代码将失败。
if (newLast < last) {
它将进入无限递归
引用包含重复数组作为输入的代码段
function sort(arr) {
if (arr.length === 2) {
const v1 = arr[0];
const v2 = arr[1];
const isGreater = (
(isString(v1) && isString(v2) && v1.toString().toLocaleCompare(v2) > 0) ||
(isNumber(v1) && isNumber(v2) && v1 > v2)
);
return isGreater ? [ v2, v1 ] : [ v1, v2 ];
} else {
const last = arr.pop();
const ret = sort(arr);
const newLast = ret.peekLast();
debugger;
if (newLast < last) {
return [ ...ret, last ];
} else {
return sort( [ last, ...ret ] );
}
}
}
function isString(value) { return typeof value === 'string'; }
function isNumber(value) { return Number.isFinite(value); }
Array.prototype.peekLast = function () { return this.slice().pop(); }
//console.log(sort([1,2,3,4,5]))
console.log(sort([3,3,5,2]))
答案 2 :(得分:1)
我看到中间值创造的脉络并非无关紧要。
peekLast
调用Array.prototype.slice
来复制数组。您复制整个数组只是为了返回最后一个元素。
Array.prototype.peekLast = function () { return this.slice().pop(); }
Array.prototype.peekLast = function () { return this[this.length]; }
这使您每次都获得相同的结果,而无需复制。
在[ ...arr, x ]
之类的表达式中使用扩展参数会完全复制arr
。
arr.concat([ x ])
做同样的事情而没有复制(或变异)arr
您调用peekLast
,并在输入中每个元素使用...x
一次。仅在这些操作上,仅在100个项目的列表上调用sort
将复制10,000个以上的元素。仅1,000个项目的列表将复制超过1,000,000个元素。算法改进的空间?当然可以。
马克·迈耶(Mark Meyer)从右脚开始。如果要使用递归,最好以函数样式编写程序,因为它将产生最佳结果。将命令式(陈述,突变,重新分配,其他副作用等)与递归相结合是偏头痛的秘诀。
Mark的算法,无论“代码改进” 有多大,您的问题是要求“算法改进” 。在这种情况下,马克的算法通过使用许多...x
表达式而遭受类似的中间值创建。
另一个潜伏的罪行是在同一阵列.filter
上重复使用rest
。这会导致效率低下,因为每个元素要完全rest
重复两次(2)次。这是达到低挂起的内置函数的征兆,这些函数可以 close 接近所需的内容,而不能完全所需的内容。一个更好的函数将遍历数组一次并返回 both 结果。
由于代码质量的显着提高,Mark程序的效率低下大部分可以原谅。他的程序比您的程序更具可读性,因为他使用的是函数样式,而这正是递归的来源。低效率也很容易解决,所以也许这对您来说是一个练习?
让我们看看您的大脑是否运转正常。在让您窒息的过多信息之前,我们将看看别人提交的答案。