面试题17:打印从1到最大的n位数。输入数字n,按顺序打印出从1到最大的n位十进制数。比如输入3,则打印出1、2、3、…、999。
我们很容易想到以下解决思路:
#include <iostream>
using namespace std;
void Print1ToMaxNDigits(int n) {
int num = 1; //存放最大的n位数+1
int i = 0;
while (i < n) {
num *= 10;
++i;
}
for (int i = 1; i < num; ++i) {
cout << i << endl;
}
}
int main() {
Print1ToMaxNDigits(3);
}
但n的范围没有规定,当输入的n很大时,会造成溢出,因此解决这个问题需要表示大数,可以用字符串或数组表示,以下思路用字符串解决大数问题。
用字符串表示数字时,每个字符表示一位,每个字符取’0’~‘9’中的一个字符,由于数字的最大位数为n,我们需要一个长为n+1的字符串(最后一位保存’\0’),当实际数字不够n位时,在字符串前半部分补0。首先把字符串中每一个数字都初始化为’\0’,然后每一次为字符串表示的数字+1,之后再打印出来。我们需要做的有两件事,一是在字符串表达的数字上模拟加法,二是把表达式表示的数字打印出来。
我们需要一个函数在表示数字的字符串上加一,并且要知道什么时候停止加一,即什么时候到了最大的n位数(n个9),一个最简单的方法是每次递增之后都调用库函数strcmp比较表示数字的字符串和最大的n位数“999…99”,如果相等就表示已经到了最大的n位数,虽然调用strcmp函数很简单,但对于长度为n的字符串,每次的时间复杂度为O(n)。我们注意到只有对“999…99”加一的时候才会在第一个字符上产生进位,因此,当我们发现加一时第一个字符产生了进位就说明加一之前的字符串已经是最大的n位数了,这种判断方法时间复杂度为O(1)。
接下来考虑怎样输出字符串表示的数字,虽然可以直接打印整个字符串,但当数字不够n位时,前面会补0,这些补位的0不应该被打印出来。
完整代码如下:
#include <iostream>
using namespace std;
bool Increment(char* num) {
bool isOverflow = false; //是否到达最大值
int takeOver = 0; //进位
int len = strlen(num); //strlen返回不带'\0'的字符串长度
for (int i = len - 1; i >= 0; --i) {
int iNum = num[i] - '0' + takeOver; //第i个数字加上进位后的结果
if (i == (len - 1)) { //第一次循环时最低位加一
++iNum;
}
if (iNum == 10) { //如进位后该位数字为10,说明要进位
if (i == 0) { //若当前为最高位且需要进位,说明原串已经是最大数字了,不需要进行操作
isOverflow = true;
}
else { //不是最高位时进位
takeOver = 1; //进位设为1
num[i] = '0'; //进位后该位置值变为0
}
}
else { //如第i位置的数字不需进位
num[i] = '0' + iNum;
break; //第i位置之前的也不需要动了,跳出循环即可
}
}
return isOverflow;
}
void PrintStrNumber(char* num) {
bool isNumber = false; //是否当前字符是数字部分
int len = strlen(num);
for (int i = 0; i < len; ++i) {
if (num[i] != '0') { //数字由从左往右遇到第一个非0值开始
isNumber = true;
}
if (isNumber) {
cout << num[i];
}
}
cout << endl;
}
void Print1ToMaxNDigits(int n) {
if (n <= 0) {
return;
}
char* num = new char[n + 1];
memset(num, '0', n);
num[n] = '\0';
while (!Increment(num)) {
PrintStrNumber(num);
}
delete[] num;
}
int main() {
Print1ToMaxNDigits(3);
}
由于我们模拟了整数的加法,代码长度很长,面试时很难写出,现在换一个思路。n位所有十进制数就是每一位从0到9的全排列。我们把数字每一位从0到9排列一遍就得到了所有的十进制数:
#include <iostream>
using namespace std;
void PrintStrNumber(char* num) {
bool isNumber = false;
int len = strlen(num);
for (int i = 0; i < len; ++i) {
if (num[i] != '0') {
isNumber = true;
}
if (isNumber) {
cout << num[i];
}
}
if (isNumber) { //当传入的值为全0的字符串时,不打印任何内容,如没有if语句,会输入0会打印空行
cout << endl;
}
}
void Print1ToMaxNDigitsRecursively(char* num, int length, int index) {
if (index == length - 1) { //如果传入的索引值到了最大,打印
PrintStrNumber(num);
return;
}
for (int i = 0; i < 10; ++i) {
num[index + 1] = '0' + i; //传入的索引+1位从0到9循环
Print1ToMaxNDigitsRecursively(num, length, index + 1);
}
}
void Print1ToMaxNDigits(int n) {
if (n <= 0) {
return;
}
char* num = new char[n + 1];
memset(num, '0', n);
num[n] = '\0';
for (int i = 0; i < 10; ++i) {
num[0] = '0' + i; //第一位从0到9循环
Print1ToMaxNDigitsRecursively(num, n, 0);
}
}
int main() {
Print1ToMaxNDigits(3);
}