剑指offer 学习笔记 打印从1到最大的n位数
面试题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);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)