JS中的递归排序

时间:2019-01-02 06:18:07

标签: javascript algorithm

在一次采访中,我被要求编写一个程序/算法以使用递归对数字数组进行排序。

尽管我隐约回答了它,但我尝试并提出了以下代码:

您可以使用以下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]))


我实施的算法是:

  • 获取数组并检查其长度是否大于2。
  • 如果是,
    • 删除最后一个元素并将其存储在变量中。
    • 再次调用没有最后一个元素的相同函数,直到它具有2个项目为止。
    • 从递归调用返回的接受数组并查看最后一个元素。
    • 如果newLast的值大于previousLast
      • previousLast作为第一个元素,然后再次使用此数组进行调用。
    • 如果没有,请按previousLast进行数组并返回它。
  • 其他,
    • 对于数字和字符串,请检查相等性并返回正确的顺序。
    • 对于其他任何内容,返回相同的值

问题是,是否有更好的方法来实现(算法明智的)?

注意:我不希望代码有所改进。这个问题的目的是改善算法部分或我错过的任何常规内容。

我也知道,当前代码不支持:

  • 排序。它将仅按升序排序。
  • 对于Date对象可能会中断,并且通常不支持Objects。

谢谢!

3 个答案:

答案 0 :(得分:2)

我认为,大多数访问者都希望您针对此问题回答quicksortmerge 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)

我看到中间值创造的脉络并非无关紧要。

  1. peekLast调用Array.prototype.slice来复制数组。您复制整个数组只是为了返回最后一个元素。

    Array.prototype.peekLast = function () { return this.slice().pop(); }
    Array.prototype.peekLast = function () { return this[this.length]; }
    

    这使您每次都获得相同的结果,而无需复制。

  2. [ ...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程序的效率低下大部分可以原谅。他的程序比您的程序更具可读性,因为他使用的是函数样式,而这正是递归的来源。低效率也很容易解决,所以也许这对您来说是一个练习?

让我们看看您的大脑是否运转正常。在让您窒息的过多信息之前,我们将看看别人提交的答案。