(说明:本博客中的题目、题目详细说明及参考代码均摘自 “何海涛《剑指Offer:名企面试官精讲典型编程题》2012年”)
题目
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数.比如输入 3,则打印出 1,2,3 一直到最大的 3 位数即 999 .
算法设计思想
由于最大的 n 位十进制可能超过整型范围的限制,而成为大数问题.本题目的关键是如何实现大数的表示或运算.本博客采用参考书中的两种方法,将从 1 到最大 n 位数之间的所有数都看作 n 位数,实际的数若不足 n 位,则在前补 0.具体的设计思想如下 :
1) 使用字符串模拟数字加法,从 1 开始递增到最大 n 位数.
在计算机中,n 位数可用包含 n 个指定字符( '0' - '9' )的字符串(所有字符均为 '0' 除外)表示.其可以想象为对字符串实现伪码:
for ( i = 1; i < max_n_digits; i++ ) print i;
2) 将 n 位数看做是 n 个数(0 - 9)的排列问题.
n 位数的排列问题,即 n 位数的每一位都可取 10 个数(0 - 9)中任意一个数,对于 n 位数,共有 10^n 个选择,注意需要去掉所有位都是 0 的排列,与上一个方法的输出结果相同.
易错点:在打印每个数时,打印前导零是没有意义的.其中,前导零是第一个非零元素的最高有效位之前的所有零.
C++实现
/*
* Author: klchang
* Date: 2018.2.26
* Description: Print digits from 1 to the maximum n digits.
*/
#include <iostream>
#include <string>
// Check if the string contains illegal characters
bool checkDigitString(std::string numeric_str)
{
bool isLegal = true;
std::basic_string<char>::iterator iter = numeric_str.begin();
for (; iter != numeric_str.end(); ++ iter) {
char ch = *iter;
if (ch < '0' || ch > '9') {
isLegal = false;
break;
}
}
return isLegal;
}
// Remove the leading zeros in a numeric string
std::string removeLeadingZeros(std::string numeric_str)
{
int i = 0;
size_t len = numeric_str.length();
// Return null string when including illegal characters
if (!checkDigitString(numeric_str)) {
std::cout << "Input string " << numeric_str << " contains at least an illegal character." << std::endl;
return "";
}
for (; i < len; ++i) {
if (!(numeric_str[i] == '0'))
break;
}
if (i >= len) {
numeric_str = "0";
} else {
numeric_str = numeric_str.substr(i);
}
return numeric_str;
}
// Simulate the numeric operation that numeric string adds one
std::string incrementByOne(std::string& numeric_str)
{
size_t len = numeric_str.size();
std::string output_str(numeric_str);
if (len <= 0)
return output_str;
int carry = 0;
bool lowest_bit = true;
std::basic_string<char>::reverse_iterator riter = output_str.rbegin();
for (; riter != output_str.rend(); ++ riter) {
int value = *riter - '0';
if (lowest_bit) {
lowest_bit = false;
value ++;
}
value += carry;
carry = 0; // clear carry
if (value > 9) {
carry = 1;
value -= 10;
}
*riter = '0' + value; // update correspondent characters
if (carry <= 0) break;
}
// pass the length of number string
if (carry > 0) {
output_str = std::string("1") + output_str;
}
return output_str;
}
// Compare the two numeric strings
// Return value: int,
// 1 when s1 > s2; 0 when s1 == s2; -1 when s1 < s2
int compare(std::string s1, std::string s2)
{
int result = 0;
std::string valid_s1, valid_s2;
valid_s1 = removeLeadingZeros(s1);
valid_s2 = removeLeadingZeros(s2);
size_t len_1 = valid_s1.size();
size_t len_2 = valid_s2.size();
if (len_1 > len_2) {
result = 1;
} else if (len_1 < len_2) {
result = -1;
} else {
std::basic_string<char>::iterator iter1 = valid_s1.begin();
std::basic_string<char>::iterator iter2 = valid_s2.begin();
for (; iter1 != valid_s1.end(); ++iter1, ++iter2 ) {
if (*iter1 == *iter2) {
continue;
} else if (*iter1 > *iter2) {
result = 1;
} else {
result = -1;
}
break;
}
}
return result;
}
// Print the digits without the leading zeros
void printDigits(std::string digits)
{
std::string out_str = removeLeadingZeros(digits);
if (out_str != "0") {
std::cout << out_str << std::endl;
}
}
// Print N digits of length `length` starting from index start
void printNDigitsRecursively(char* digits, int length, int start)
{
if (length == start) {
std::string cur_number = digits;
printDigits(cur_number);
return;
}
// Set the digit of the start index
for (int i = 0; i < 10; ++ i) {
digits[start] = i + '0';
printNDigitsRecursively(digits, length, start+1);
}
}
// print digits from 1 to maximum
void printNDigits(int n, int method=0)
{
if (n <= 0) {
std::cout << "ERROR: Illegal parameters n <= 0!" << std::endl;
return;
}
if (method == 1) {
// Recursive method
std::cout << "
Use the recursive method to print the numbers from 1 to maximum n digits: " << std::endl;
int start = 1;
char* digits = new char[n+1];
digits[n] = '