剑指offer 题17——数值的整数次方

数值的整数次方

题目: 输入数字n,按顺序打印出从1到最大的n位十进制数。比如输入3,则打印出1,2,3一直到最大的3位数999.

1. 陷阱
乍一看,似乎很简单,一次循环就能搞定

void PrintToMaxOfNDigits_1(int n)
{
	int number=1;
	int i=0;
	while(i++<n)
		number*=10;
	
	for(i=1;i<number;++i)
		print("%d\t",i);
}

但是如果再仔细想想,因为没有给定n的范围,所以如果n是个大数,那么无论是 整型(int) 或者 长整型(long long) 还是会溢出。

所以其实这是一个大数问题!
大数问题用什么?
当然是——字符串!

2. 在字符串上模拟数字加法
因为数字最大的是n位,所以我们需要一个长度为n+1的字符串(字符串的最火一位是结束符为 \0),当实际数字不够n为时,在字符串的前半部分补0。
首先把字符串的每一个数字初始化为‘0’。然后每一次为字符串表示的数字加1,再打印出来。因此我们只需要做两件事:一是字符串表达的数字上模拟加法;二是把字符串表达的数字打印出来
根据以上思想,有如下代码:

void PrintToMaxOfNDigits(int n)
{
	if(n<=0)
		return;
	char* number=new char[n+1];
	memset(number,'0',n);
	number[n]='\0';
	
	while(!Increment(number))
	{
		PrintNumber(number);
	}
	delete []number;
}

其中函数Increment表示数字的字符串number上增加1,而函数PrintNumber便是打印number

现在关键是两个函数的书写

a.Increment函数
如何知道什么时候停止number上增加1,即什么时候到了最大的n位数"9999…999",最简单的就是在每次增加之后,都调用strcmp函数比较字符串number和最大的“9999…999”,虽然调用strcmp很简单,答案是对于长度为n的字符串,复杂度为O(n)。
有没有更好的办法?当然,可以看到,只有对“999…999”加1的时候,才会在第一个字符(下标为0)的基础上产生进位。此时就是最大的n位数,此时Increment返回true,循环停止。
代码如下:

bool Increment(char* number)
{
	bool isOverflow=false;
	int nTakeOver=0;
	int nLength=strlen(number);
	for(int i=nLength-1;i>=0;i--)
	{
		int nSum=number[i]-'0'+nTakeOver;
		if(i==nLength-1)
			nSum++;
		if(nSum>=10)
		{
			if(i==0)
				isOverflow=true;
			else
			{
				nSum-=10;
				nTakeOver=1;
				number[i]='0'+nSum;
			}
		}
		else
		{
			number[i]='0'+nSum;
			break;
		}
	}
	return isOverflow;
}

a.PrintNumber函数
当数字位数不够时,在数字前面补0,但是打印的时候不应该打印出来,所以要在第一个非0的时候才打印

void PrintNumber(char* number)
{
	bool isBeginning=true;
	int nLength=strlen(number);
	for(int i=0;i<nLength;++i)
	{
		if(isBeginning && number[i]!='0')
			isBeginning=false;
		
		if(!isBeginning)
		{
			printf("%c",number[i]);
		}
	}
	printf("\t");
}

3. 递归让代码更简洁
如果我们在数字前面补0,就会发现n位所有的十进制数其实及时n个从0到9的全排列。也就是说,我们把数字的每一位都从0到9排列一遍,就得到了所有的十进制数。只是在打印的时候,排在前面的0不打印出来就好了
全排列很容易表达,数字的每一位都可能时0~9中的一个数,然后设置下一位,递归的结束条件时我们已经设置了数字的最后的一位。

void PrintToMaxOfNDigits(int n)
{
	if(n<=0)
		return;
	char* number=new char[n+1];
	number[n]='\0';
	
	for(int i=0;i<10;++i)
	{
		number[0]=i+'0';
		PrintToMaxOfNDigitdRecursively(number,n,0);
	}
	delete[] number;
}

void PrintToMaxOfNDigitdRecursively(char* number,int length,int index)
{
	if(index==length-1)
	{
		PrintNumber(number);
		return;	
	}
	for(int i=0;i<10;++i)
	{
		number[index+1]=i+'0';
		PrintToMaxOfNDigitdRecursively(number,length,index+1);
	}
}

参考书籍:《剑指offer》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值