如何优化我的函数,它取一个正整数并返回下一个较小的正整数?

时间:2017-02-03 05:19:04

标签: javascript string algorithm math numbers

我正在尝试编写一个带正整数的函数,并返回包含相同数字的下一个较小的正整数,并且当没有包含相同数字的较小数字时返回-1。

For example:

nextSmaller(21) == 12
nextSmaller(531) == 513
nextSmaller(2071) == 2017

我编写了一个解决此问题的代码,但我真的不知道如何进一步优化它。请你帮助我好吗?它在repl.it上运行得相当快,但是当我提交它时,它表示它需要超过1200毫秒并且不允许我提交它,即使所有测试都通过了。

function nextSmaller(n) {
var nArray= n.toString().split("")
var minimumNum = 1 + Array(nArray.length).join('0')
for(var i=n-1; i >= minimumNum; i--) {
  var newNumArray = i.toString().split('');
  var counter = 0;
  for (var j=0; j<newNumArray.length; j++) {
    if (nArray.indexOf(newNumArray[j]) > -1) {
        counter++
        nArray.splice(nArray.indexOf(newNumArray[j]), 1)
        if (counter === n.toString().split("").length) {
        return i;
        }
     } 
  }
       nArray = n.toString().split("");
       if (i === Number(minimumNum)) return -1;
  }
}

2 个答案:

答案 0 :(得分:3)

你的代码可以稍微优化一下,例如你可以在你的内循环中使用break语句,一旦你知道当前的一个不起作用(它应该让它运行)就转移到下一个数字在大约一半的时间内,但n 91234567并且在循环中代替n.toString().split("").length仍然相当慢,请使用变量,因此您只需要将n转换为数组一次。

function nextSmaller(n) {
  var nArray = n.toString().split("")
  var length = nArray.length;
  var minimumNum = 1 + Array(length).join('0')
  for(var i=n-1; i >= minimumNum; i--) {
    var newNumArray = i.toString().split('');
    var counter = 0;
    for (var j=0; j<newNumArray.length; j++) {
      if (nArray.indexOf(newNumArray[j]) < 0)
          break;
      counter++
      nArray.splice(nArray.indexOf(newNumArray[j]), 1)
      if (counter === length) {
          return i;
      }
    }
    nArray = n.toString().split("");
  }
  return -1;
}

有一种非常有效的计算下一个排列的算法,可以很容易地调整以获得前一个排列(如果结果排列以0开始,则返回-1)。我改编this algorithm来做到这一点:

[21,531,2071,912345678,9123545678,915345678].forEach( x => console.log( nextSmaller( x ) ) );

function nextSmaller(n) {
  
    const arr = ( n + '' ).split( '' ).map( Number );
  
    // Find longest non-decreasing suffix
    let i, prev = 9;
    for ( i = arr.length; i--; ) {
        if ( arr[ i ] > prev )
            break;
        prev = arr[ i ];
    }
  
    // If whole sequence is non-decreasing,
    // it is already the smallest permutation
    if ( i < 0 )
        return -1;
  
    const pivot_i = i;
    const pivot = arr[ pivot_i ];
  
    for ( i = arr.length; i--; ) {
        if ( arr[ i ] < pivot )
            break;
    }
  
    arr[ pivot_i ] = arr[ i ];
    arr[ i ] = pivot;

    if ( arr[ 0 ] === 0 )
        return -1;
  
    return +arr.slice( 0, pivot_i + 1 ).concat( arr.slice( pivot_i + 1 ).reverse( ) ).join('');
}

答案 1 :(得分:2)

算法可能如下所示:

  1. 对于输入数字n,查找由permutationssame digits这些数字sort形成的所有数字。例如,如果n=213,我们将排序列表作为[123, 132, 213, 231, 312, 321]。 (例如,Permutations in JavaScript?可以帮助您)。

  2. 在排序列表中查找数字index i的{​​{1}}。如果n返回i>0处的号码,则返回index i-1(如果它是排序列表第一个位置出现的最小数字)。

  3. 另一种替代算法可能如下:

    减去数字-1直到除非您找到一个具有完全相同的数字(以不同的顺序,您可以对数字进行排序并检查相等性)。

    效率最高的将类似于@Paulpro(https://www.nayuki.io/page/next-lexicographical-permutation-algorithm)提到的那个

    1. n的十进制字符串表示中找到longest non-decreasing suffix
    2. 如果整个字符串n不递减,则返回-1(不能有任何小字符串)。
    3. 否则,请选择后缀开头的数字n,并将其与pivot中的the leftmostthe largest数字交换为{suffix 1}}比smaller。退回这个号码。