我刚刚搞砸了一次面试,而且我的面试问题几乎毫无进展。

给定一个数,找出下一个与之完全相同的更大的数 作为原始数字的数字集。例如:给定38276返回 38627

我想从找到第一个比个位小的数字的下标开始。然后我将旋转子集中的最后一个数字,这样它是由相同的数字组成的下一个最大的数字,但卡住了。

面试官还建议我试着一次换一个数字,但我搞不懂算法,只能盯着屏幕看20-30分钟。不用说,我想我还得继续找工作。


当前回答

这是个很有趣的问题。

这是我的java版本。在我检查其他贡献者的评论之前,从弄清楚模式到完全完成代码,我花了大约3个小时。很高兴看到我的想法和别人一样。

O (n)的解决方案。老实说,如果时间只有15分钟,并且要求在白板上完成完整的代码,我将会失败。

以下是我的解决方案的一些有趣点:

避免任何排序。 完全避免字符串操作 实现O(logN)空间复杂度

我在代码中添加了详细注释,并在每个步骤中添加了大O。

  public int findNextBiggestNumber(int input  )   {
    //take 1358642 as input for example.
    //Step 1: split the whole number to a list for individual digital   1358642->[2,4,6,8,5,3,1]
    // this step is O(n)
    int digitalLevel=input;

    List<Integer> orgNumbersList=new ArrayList<Integer>()   ;

    do {
        Integer nInt = new Integer(digitalLevel % 10);
        orgNumbersList.add(nInt);

        digitalLevel=(int) (digitalLevel/10  )  ;


    } while( digitalLevel >0)    ;
    int len= orgNumbersList.size();
    int [] orgNumbers=new int[len]  ;
    for(int i=0;i<len;i++){
        orgNumbers[i ]  =  orgNumbersList.get(i).intValue();
    }
    //step 2 find the first digital less than the digital right to it
    // this step is O(n)


    int firstLessPointer=1;
    while(firstLessPointer<len&&(orgNumbers[firstLessPointer]>orgNumbers[ firstLessPointer-1 ])){
        firstLessPointer++;
    }
     if(firstLessPointer==len-1&&orgNumbers[len-1]>=orgNumbers[len-2]){
         //all number is in sorted order like 4321, no answer for it, return original
         return input;
     }

    //when step 2 step finished, firstLessPointer  pointing to number 5

     //step 3 fristLessPointer found, need to find  to  first number less than it  from low digital in the number
    //This step is O(n)
    int justBiggerPointer=  0 ;

    while(justBiggerPointer<firstLessPointer&& orgNumbers[justBiggerPointer]<orgNumbers[firstLessPointer]){
        justBiggerPointer++;
    }
    //when step 3 finished, justBiggerPointer  pointing to 6

    //step 4 swap the elements  of justBiggerPointer and firstLessPointer .
    // This  is O(1) operation   for swap

   int tmp=  orgNumbers[firstLessPointer] ;

    orgNumbers[firstLessPointer]=  orgNumbers[justBiggerPointer]  ;
     orgNumbers[justBiggerPointer]=tmp ;


     // when step 4 finished, the list looks like        [2,4,5,8,6,3,1]    the digital in the list before
     // firstLessPointer is already sorted in our previous operation
     // we can return result from this list  but  in a differrent way
    int result=0;
    int i=0;
    int lowPointer=firstLessPointer;
    //the following pick number from list from  the position just before firstLessPointer, here is 8 -> 5 -> 4 -> 2
    //This Operation is O(n)
    while(lowPointer>0)        {
        result+= orgNumbers[--lowPointer]* Math.pow(10,i);
        i++;
    }
    //the following pick number from list   from position firstLessPointer
    //This Operation is O(n)
    while(firstLessPointer<len)        {
        result+= orgNumbers[firstLessPointer++ ]* Math.pow(10,i);
        i++;
    }
     return  result;

}

下面是在Intellj中运行的结果:

959879532-->959892357
1358642-->1362458
1234567-->1234576
77654321-->77654321
38276-->38627
47-->74

其他回答

一个几乎相同的问题出现了Code Jam问题,这里有一个解决方案:

http://code.google.com/codejam/contest/dashboard?c=186264#s=a&a=1

下面用一个例子总结一下这个方法:

34722641

A.将数字序列分成两部分,使右边的部分尽可能长,同时保持递减顺序:

34722 641

(如果整个数字是递减的,就没有比这个数字更大的数字了。)

在这一点上,你知道没有从左边开始的更大的数了,因为右边的剩余数字已经尽可能大了。

责任。选择第一个序列的最后一位:

3472(2) 641

B.2。找出第二个序列中比它大的最小的数字:

3472(2) 6(4)1

你要做的就是找到左边可能的最小增量。

B.3。交换:

3472(2) 6(4)1
->
3472(4) 6(2)1
->
34724 621

C.将第二个序列按递增顺序排序:

34724 126

d .完成了!

34724126

你把这个数字分开,这样你就知道没有更大的数字具有相同的左边部分,你把左边部分增加了尽可能小的量,你让剩下的右边部分尽可能小,所以你可以确保这个新数字是用相同的数字集合可以得到的最小的更大的数字。

给定n位数字加9。然后检查它是否在限制范围内(第一个(n+1)位数)。如果是,则检查新号码中的数字是否与原号码中的数字相同。 重复加9,直到两个条件都为真。 当数字超过限制时停止算法。

对于这种方法,我想不出一个与之相矛盾的测试用例。

关于如何做到这一点,请参阅Knuth的“计算机编程艺术:生成所有排列”(.ps.gz)中的“算法L”。

 private static int GetNextHigherNumber(int num)
        {
            //given 38276 return 38627

            string numberstring = num.ToString();

            char[] sNum = numberstring.ToCharArray();

            for (int i = sNum.Length - 1; i > 0; i--)
            {
                for (int j = i - 1; j > 0; j--)
                {
                    if (sNum[i] > sNum[j])
                    {
                        for (int x = i; x > j; x--)
                        {
                            char chr = sNum[x]; 
                            sNum[x] = sNum[x - 1];
                            sNum[x - 1] = chr;
                        }

                        i = 0;
                        break;
                    }
                }
            }

            numberstring = string.Empty;
            for(int x= 0 ; x<sNum.Length;x++)
            {
                numberstring += sNum[x].ToString();
            }

            return Convert.ToInt32(numberstring);
        }

你可以用O(n)(其中n是位数)这样做:

从右边开始,找到左位数小于右位数的第一对数字。让我们用“digit-x”来表示左边的数字。在数字-x的右边找到比数字-x大的最小的数,并把它放在数字-x的左边。最后,按升序对剩余的数字进行排序——因为它们已经是降序的,所以你所需要做的就是将它们颠倒(除了digit-x,它可以放在O(n)中正确的位置)。

举个例子可以更清楚地说明这一点:

123456784987654321
start with a number

123456784 987654321
         ^the first place from the right where the left-digit is less than the right  
         Digit "x" is 4

123456784 987654321
              ^find the smallest digit larger than 4 to the right

123456785 4 98764321
        ^place it to the left of 4

123456785 4 12346789
123456785123446789
         ^sort the digits to the right of 5.  Since all of them except 
         the '4' were already in descending order, all we need to do is 
         reverse their order, and find the correct place for the '4'

正确性证明:

让我们用大写字母来定义数字字符串,用小写字母来定义数字。语法AB表示“字符串A和B的连接”。<是字典排序,当数字字符串长度相等时,它与整数排序相同。

原始数N是AxB的形式,其中x是个位数,B是降序的。 我们的算法找到的数字是AyC,其中y∈B是最小的数字> x(由于x的选择方式,它必须存在,见上文),C是升序排序的。

假设有一些数字(使用相同的数字)N'使得AxB < N' < AyC。N'必须以A开头,否则它不可能在它们之间,所以我们可以把它写成AzD的形式。现在我们的不等式是AxB < AzD < AyC,它等价于xB < zD < yC,其中所有三个数字字符串都包含相同的数字。

为了使它成立,我们必须有x <= z <= y,因为y是最小的数字> x, z不能在它们之间,所以z = x或z = y,假设z = x,那么我们的不等式是xB < xD < yC,这意味着B和D都有相同的数字。但是,B是降序的,所以没有比它大的数字的字符串。因此,我们不能让B < D,按照同样的步骤,我们可以看到,如果z = y,我们不能让D < C。

因此N'不存在,这意味着我们的算法正确地找到了下一个最大的数字。