问题举例:
当n为3时,则打印 1~999。
代码实现:
void PrintToMaxOfNDigits_1(int n)
{
int number = 1;
int i = 0;
while (i++ < n) //假如n = 3,则number = 1000
number *= 10;
for (int i = 1; i < number; i++)
printf("%d\t", i);
}
int main()
{
int n;
cin >> n;
cout << endl;
PrintToMaxOfNDigits_1(n);
}
上述实现看似可以解决问题,但是如果数字超出了 int甚至超出了long long所能表达的范围,这样的话,问题就成了大数问题:
大数情况下的代码实现:
void PrintNumber(char *number) //打印字符串表示的数字
{
int len = strlen(number);
bool isBeginning0 = true;
for (int i = 0; i < len; i++)
{
if (isBeginning0 && number[i] != '0') //找到第一个不为'0'的字符
isBeginning0 = false;
if (!isBeginning0) //打印
printf("%c", number[i]);
}
printf("\t");
}
bool Increment(char *number) //大数加 1
{
int nTakeOver = 0; //表示进位数,开始为0,此程序当不需要进位时就会在 if(nSum<10) 处退出
bool isOverflow = false;
int len = strlen(number);
for (int i = len - 1; i >= 0; i--) //将一个数加 1,末尾加,需要进位则进位,不需要则退出;考虑越界可能性的存在,越界则isOverflow=true,表示越界
{
int nSum = number[i] - '0' + nTakeOver; //取出每一位的值
if (i == len - 1) //如果此时是数字的个位,则加 1,只执行一次,即循环的第一次
nSum++;
if (nSum < 10) //位上加 1后,如果小于10则将加后的结果写入,且目的达到,退出循环
{
number[i] = nSum + '0';
break;
}
else //加 1后如果不小于10需要继续向高位进位
{
if (i == 0) //如果此时是最高位需要进位,则意味着这个数字已经越界
isOverflow = true; //标志位置为true,表示已经越界
else //如果不是最高位需要进位,或者此时还不是最高位(i>1 && i<len-1),则执行进位操作
{
nSum -= 10;
nTakeOver = 1; //进位的大小,因为此程序每次都是加 1,所以即便有进位,进位的数也是 1.
number[i] = nSum + '0'; //将本位的数写回
}
}
}
return isOverflow;
}
void PrintToMaxOfNDigits(int n)
{
if (n <= 0)
return;
char *number = new char[n + 1];
memset(number, '0', n);
number[n] = '\0';
PrintNumber(number);
while (!Increment(number)) //加一后未越界
PrintNumber(number); //打印数据
delete[] number;
}
int main()
{
int n;
cin >> n;
cout << endl;
PrintToMaxOfNDigits(n);
}
关于上述代码的分析:
我们在做大数加法时,需要知道何时停止加 1,一个简单的办法是让大数(例如:100)与n位的最大数(例如:999)已经相等,则不必再加,但是每次加一都要使用strcmp() 例如:(“999”,number)来作比较,但是这样做的话每次都是O(n),且比较n次。
第二种方法:我们知道当n等于3,数值达到999时,再进位就会越界,我们可以以此来作为评判。如果加后越界,则函数Increment()返回true,对应就是已经越界。
如果觉得上述代码难以理解,则可用下列方式来实现:
打印的思路不变!新思路:将问题转换为数字排列的问题;我们发现如果在数字的前面补0,就会发现 n位所有十进制数其实就是 n个从 0到 9的全排列。也就是说,我们把数字的每一位都从 0到 9排列一遍,就得到了所有的十进制数。
void PrintNumber(char *number) //打印字符串表示的数字
{
int len = strlen(number);
bool isBeginning0 = true;
for (int i = 0; i < len; i++)
{
if (isBeginning0 && number[i] != '0') //找到第一个不为'0'的字符
isBeginning0 = false;
if (!isBeginning0) //打印
printf("%c", number[i]);
}
printf("\t");
}
void PrintToMaxOfNDigitsRecursively(char *number, int length, int index)
{
if (index == length - 1) //当index==length-1说明到了数的最后一位
{
PrintNumber(number);
return;
}
for (int i = 0; i < 10; ++i)
{
number[index + 1] = '0' + i;
PrintToMaxOfNDigitsRecursively(number, length, index + 1); //递归实现
}
}
void PrintToMaxOfNDigits(int n)
{
if (n <= 0)
return;
char *number = new char[n + 1]; //申请n+1个位置
number[n] = '\0';
for (int i = 0; i < 10; i++)
{
number[0] = i + '0'; //0下标可取值为 0~9
PrintToMaxOfNDigitsRecursively(number, n, 0); //从0下标开始
}
delete[] number;
}
int main()
{
int n;
cin >> n;
cout << endl;
PrintToMaxOfNDigits(n);
}
代码的过程如下:
而当n特别大时不适宜用递归,层次太深,可能会导致栈溢出