最近在研究<<剑指offer>>这本书,觉得这本书是必须要看的一本书,它里面讲的面试题真的很经典,下面我们就<<剑指offer>>中的面试题12:打印1到最大的n位数来看看面试官一般喜欢出什仫样的陷阱?
【题目描述】:
输入数字n,按顺序打印出从1到最大的n位十进制数,
比如输入3,则打印出1,2,3...一直到最大的3位数,即999;
【问题分析】:
刚看到这道题的时候我就暗暗窃喜了,这还不容易吗?我只要求出最大的n位数即999然后循环输出就可以了,于是我立刻写下了下面的代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
void print_maxnum(int n) //如果n足够大存在缺陷
{
int i=0;
if(n <= 0)
return ;
for(i=1;i<pow(10.0,n);i++)
{
printf("%d ",i);
}
}
int main()
{
int num=0;
printf("请输入一个你要输出的最高的位数:");
scanf("%d",&num);
print_maxnum(num);
system("pause");
return 0;
}
当我们满心欢喜的以为自己完成了任务的时候却发现如果输入的num足够大,不管我们用整形(int)还是长整型(long long)都会溢出呢?
此时我们考虑的就是大数的问题了? 这就是面试官在这道题里设置的
第一个陷阱 。我们知道数组和字符串都可以用于表示一个足够大的数字,那仫我们是不是可以考虑用字符串来模拟加法运算呢?当然可以了~~
1). 用字符串表示大数的时候,如果我们需要输出的是最大的n位十进制数那仫我们需要分配的字符串的长度是(n+1),最后一位用来存储字符串结束标志'\0';
2).分配好了内存之后就是如何在字符串上模拟实现加法运算了,在这里其实也有面试官设置的小技巧,我们可以根据十进位的加法运算来联想字符串的加法运算,我们知道十进制的加法是从个位开始的当然模拟字符串的加法也从最后一个字符开始啦!此时我们就需要先把该字符取出来转化为整形数字,我们知道在字符串操作中给一个整形数字加(字符0)就是把它转化为字符,当然给一个字符减去(字符0)就可以把它转化为数字了;如果确实是最后一位字符那仫就把该数字加1( 需要注意的是我们每次都是从最后一个字符开始加起)到这里我们就不得不考虑加法的进位了,只有当字符对应的数字加到10或者比10大时我们需要进位,否则就直接将该数字转化为字符存储到对应字符的位置中去; 在字符所转化的数字进位中不得不考虑的就是如果此时已经是第一个字符了那仫还需要进位吗?当然不需要否则就溢出了;当然如果是普通的情况我们就只需要将该位对应的数字减去10并将进位信号置为1可以了 ;
3).如何输入呢?直接用printf?这就又掉入面试官的陷阱里去了这是面试官设置的第二个陷阱,我们知道在上述思路中当数字不够n位时我们是在前面几位补零,如果我们直接输出,那仫就把前面的零也一起输出了,这当然不符合我们的实际了, 所以这里也是值得我们注意的一个关键点;
好了分析了这仫久下面就让我们来实现它吧!
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void Print(char *number) //输出字符串
{
while(*number == '0')
{
number++;
}
printf("%s ",number);
}
int PrintRecur(char *number) //字符串模拟加法
{
int sz=strlen(number);
int i=0;
int num=0;
int taken=0; //进位信号
for(i=sz-1;i>=0;i--) //从字符数组的最后一位开始
{
num=number[i]-'0'+taken;
taken=0;
if(i == sz-1)
{
num+=1;
}
if(num >= 10) //产生进位
{
if(i == 0) //是最高位
return 0;
else //不是最高位
{
num=num-10;
number[i]=num+'0';
taken=1;
}
}
else //没有产生进位
{
number[i]=num+'0';break;
}
}
return 1;
}
int main()
{
char *number=NULL;
int n=0;
printf("请输入一个你要输出的最高的位数:");
scanf("%d",&n);
number=(char *)malloc((n+1)*sizeof(char));
if(number == NULL)
{
printf("out of menory\n");
exit(EXIT_FAILURE);
}
memset(number,'0',n*sizeof(char));//将字符串初始化为全零
number[n]='\0';
while(PrintRecur(number) == 1)
{
Print(number);
}
free(number);
system("pause");
return 0;
}
写下了这样的代码是不是觉得终于完成任务了呢?当然不?其实开始这道题我自己想的就是一个n位数它的每一位无非就是0~9这10个数字,如果我们把每个数字的每一位都从0~9排列一遍就得到了所有的十进制数,在最后打印的时候同上述模拟字符串加法一样:不能把补上的零打印出来,模拟输出的思路同上;这样的思路是不是比上述模拟实现字符串加法更加方便呢?下面就让我们来实现这样思路:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void Print(char *number) //输出所有的字符值
{
while(*number == '0')
{
number++;
}
printf("%s ",number);
}
void PrintRecur(char *number,int n,int index) //全排列的方式
{
int i=0;
if(index == n) //从最后一个字符开始排列,如果排列到第一个字符就打印出来
{
Print(number);
return ;
}
for(i=0;i<10;i++)
{
number[index]=i+'0'; //控制这个字符串中的每个字符从0~9的变化
PrintRecur(number,n,index+1);
}
}
int main()
{
char *number=NULL;
int num=0;
printf("请输入一个你要输出的最高的位数:");
scanf("%d",&num);
number=(char *)malloc((num+1)*sizeof(char));
if(number == NULL)
{
printf("out of menory\n");
exit(EXIT_FAILURE);
}
memset(number,'0',num*sizeof(char));//将字符串初始化为全零
number[num]='\0';
PrintRecur(number,num,0);
free(number);
system("pause");
return 0;
}
至此整道<<剑指offer>>面试题12分析完毕,如果有什仫理解的不全面的地方请读者指出,共同学习。