代码改变世界

输出1到最大的N位数

2013-08-31 23:42  youxin  阅读(861)  评论(0编辑  收藏  举报

题目:输入数字n,按顺序输出从1最大的n位10进制数。比如输入3,则输出1、2、3一直到最大的3位数即999。

分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。

应聘者在解决这个问题的时候,最容易想到的方法是先求出最大的n位数是什么,然后用一个循环从1开始逐个输出。很快,我们就能写出如下代码:

void Print1ToMaxOfNDigits1(int n)
{
    int number=1;
    int i=0;
    while(i++<n)
        number*=10;

    for(i=1; i<number; i++)
        cout<<i<<" ";
}

初看之下,好像没有问题。但如果我们仔细分析这个问题,就能注意到这里没有规定n的范围,当我们求最大的n位数的时候,是不是有可能用整型甚至长整型都会溢出?

分析到这里,我们很自然的就想到我们需要表达一个大数。最常用的也是最容易实现的表达大数的方法是用字符串或者整型数组(当然不一定是最有效的)。

用字符串表达数字的时候,最直观的方法就是字符串里每个字符都是’0’到’9’之间的某一个字符,表示数字中的某一位。因为数字最大是n位的,因此我们需要一个n+1位字符串(最后一位为结束符号’\0’)。当实际数字不够n位的时候,在字符串的前半部分补零。这样,数字的个位永远都在字符串的末尾(除去结尾符号)。

首先我们把字符串中每一位数字都初始化为’0’。然后每一次对字符串表达的数字加1,再输出。因此我们只需要做两件事:一是在字符串表达的数字上模拟加法。另外我们要把字符串表达的数字输出。值得注意的是,当数字不够n位的时候,我们在数字的前面补零。输出的时候这些补位的0不应该输出。比如输入3的时候,那么数字98以098的形式输出,就不符合我们的习惯了。

基于上述分析,我们可以写出如下代码:

#include<iostream>
using namespace std;
// Increment a number. When overflow, return true; otherwise return false
bool increment(char* number)
{
    bool isOverflow=false;
    int nTakeOver=0;
    int nLen=strlen(number);
   // Increment (Add 1) operation begins from the end of number
    for(int i=nLen-1;i>=0;i--)
    {
        int nSum=number[i]-'0'+nTakeOver;
        if(i== nLen-1)
            nSum++;

        if(nSum>=10)
        {
            if(i==0)
                isOverflow=true;
            else
            {
                nSum-=10;
                nTakeOver=1;
                number[i]='0'+nSum;
            }
        }
        else
        {
            number[i]='0'+nSum;
            break;
        }
    }
    return isOverflow;
}

void printNumber(char number[])
{
    bool isBeginning0=true;
    for(int i=0;i<strlen(number);i++)
    {
        if(isBeginning0 && number[i]!='0')
            isBeginning0=false;
        if(!isBeginning0)
        {
            cout<<number[i];
        }
    }
    cout<<ends;
}


void Print1ToMaxOfNDigits_2(int n)
{
    if(n<=0)
        return;
    char *number=new char[n+1];
    memset(number,'0',n);
    number[n]='\0';

    while(!increment(number))
    {
        printNumber(number);
    }
    delete[] number;
}

int main()
{
    int n;
    cin>>n;
    Print1ToMaxOfNDigits_2(n);
}

用int数组来模拟:

分析:当位数较少时,在整数范围内时可由1pow10n-1 循环输出。

      但是当位数超过整数范围时会溢出,需另寻他路。中心思想是进位制,如逢十进一。

      可定义一个n位数组来模拟一个数字,或者一个n位字符串,数组中某一位的值的改变是因为后面逢10.

      具体实现如下:

#include <stdio.h>

#include <math.h>

#define POS 3
int count = 0;

void printNum(int bits[], int start, int end)

{

      int i = 0;

      for(i = start; i <= end; i++)

      {

            printf("%d",bits[i]);

      }

      printf("\t");

      count++;

      if(count % 10 == 0)

      {

            printf("\n");

      }

}

 

void initNumber(int *num, int len)

{

      int i;

      /**

       * 如果n是3位数,则设置数组初始状态为0100

       * 为什么三位数要声明为4位数组?

       * 第零位主要是用来判断截止位置,如1000,则终止

       */

      for(i = 0; i < len; i++)

      {

            num[i] = 0;

      }

      num[1] = 1;

}

 

void PrintToMaxNBits(int n)

{

      int number[n+1];

      initNumber(number,n+1);

      int i;

      while(1)

      {

 

            /**

             * 当最高位等于10时候,往前进一,以此类推

             */

            if(number[n]==10)

            {

                  i = n;

                  do{

                        number[i] = 0;

                        number[i-1]++;

                        i--;

                  }while(i>=1 && number[i]== 10);

            }

 

            /**

             * 最低位为1时推出循环

             */

            if(number[0] == 1)

            {

                  break;

            }

 

            /**

             * 每产生一个数,输出

             */

            printNum(number, 1, n);

 

            /**

             * 最高位用于递增

             */

            number[n]++;

      }

}

 

int main()

{

      int m;

      for(m = 1; m <= POS; m++)

      {

            PrintToMaxNBits(m);

      }

    return 0;

}

 

 

算法三:其实本题也就是求n位数的0~9的全排列

               第二种思路基本上和第一种思路相对应,只是把一个整型数值换成了字符串的表示形式。第二种思路虽然比较直观,但由于模拟了整数的加法,代码有点长。要在面试短短几十分钟时间里完整正确写出这么长代码,不是件容易的事情。接下来我们换一种思路来考虑这个问题。如果我们在数字前面补0的话,就会发现n位所有10进制数其实就是n个从09的全排列。也就是说,我们把数字的每一位都从09排列一遍,就得到了所有的10进制数。只是我们在输出的时候,数字排在前面的0我们不输出罢了

              全排列用递归很容易表达,数字的每一位都可能是09中的一个数,然后设置下一位。递归结束的条件是我们已经设置了数字的最后一位。

void PrintNumber(char* number)
{
    bool isBeginning0=true;
    int nLength=strlen(number);

    for(int i=0; i<nLength; i++)
    {
        if(isBeginning0 && number[i]!='0')
            isBeginning0=false;

        if(!isBeginning0)
        {
            cout<<number[i];
        }
    }
    cout<<" ";
}

void Print1ToMaxOfNDigitsRecursively(char *number, int length, int index)
{
    if(index==length-1)
    {
        PrintNumber(number);
        return;
    }
    
    for(int i=0; i<10; i++)
    {
        number[index+1]=i+'0';
        Print1ToMaxOfNDigitsRecursively(number, length, index+1);
        
    }
}

void Print1ToMaxOfNDigit3(int n)
{
    if(n<0)
        return;
    
    char *number=new char[n+1];
    number[n]='\0';
    
    for(int i=0; i<10; i++)
    {
        number[0]=i+'0';
        Print1ToMaxOfNDigitsRecursively(number, n, 0);
    }
}