打印从1到最大的n为数

问题举例:

       当n为3时,则打印 1~999。

代码实现:

void PrintToMaxOfNDigits_1(int n)
{
	int number = 1;
	int i = 0;
	while (i++ < n) //假如n = 3,则number = 1000
		number *= 10;

	for (int i = 1; i < number; i++)
		printf("%d\t", i);
}

int main()
{
	int n;
	cin >> n;
	cout << endl;
	PrintToMaxOfNDigits_1(n);
}

上述实现看似可以解决问题,但是如果数字超出了 int甚至超出了long long所能表达的范围,这样的话,问题就成了大数问题

大数情况下的代码实现

void PrintNumber(char *number) //打印字符串表示的数字
{
	int len = strlen(number);
	bool isBeginning0 = true; 
	for (int i = 0; i < len; i++)
	{
		if (isBeginning0 && number[i] != '0') //找到第一个不为'0'的字符
			isBeginning0 = false;

		if (!isBeginning0) //打印
			printf("%c", number[i]);
	}
	printf("\t");
}

bool Increment(char *number) //大数加 1
{
	int nTakeOver = 0; //表示进位数,开始为0,此程序当不需要进位时就会在 if(nSum<10) 处退出
	bool isOverflow = false;
	int len = strlen(number);

	for (int i = len - 1; i >= 0; i--) //将一个数加 1,末尾加,需要进位则进位,不需要则退出;考虑越界可能性的存在,越界则isOverflow=true,表示越界
	{
		int nSum = number[i] - '0' + nTakeOver; //取出每一位的值

		if (i == len - 1) //如果此时是数字的个位,则加 1,只执行一次,即循环的第一次
			nSum++;

		if (nSum < 10) //位上加 1后,如果小于10则将加后的结果写入,且目的达到,退出循环
		{
			number[i] = nSum + '0';
			break;
		} 
		else //加 1后如果不小于10需要继续向高位进位
		{
			if (i == 0) //如果此时是最高位需要进位,则意味着这个数字已经越界
				isOverflow = true; //标志位置为true,表示已经越界
			else //如果不是最高位需要进位,或者此时还不是最高位(i>1 && i<len-1),则执行进位操作
			{
				nSum -= 10;
				nTakeOver = 1; //进位的大小,因为此程序每次都是加 1,所以即便有进位,进位的数也是 1.
				number[i] = nSum + '0'; //将本位的数写回
			}
		}
	}
	return isOverflow;
}

void PrintToMaxOfNDigits(int n)
{
	if (n <= 0)
		return;

	char *number = new char[n + 1];
	memset(number, '0', n);
	number[n] = '\0';

	PrintNumber(number);
	while (!Increment(number)) //加一后未越界
		PrintNumber(number); //打印数据

	delete[] number;
}

int main()
{
	int n;
	cin >> n;
	cout << endl;
	PrintToMaxOfNDigits(n);
}

关于上述代码的分析:

        我们在做大数加法时,需要知道何时停止加 1,一个简单的办法是让大数(例如:100)与n位的最大数(例如:999)已经相等,则不必再加,但是每次加一都要使用strcmp() 例如:(“999”,number)来作比较,但是这样做的话每次都是O(n),且比较n次。

        第二种方法:我们知道当n等于3,数值达到999时,再进位就会越界,我们可以以此来作为评判。如果加后越界,则函数Increment()返回true,对应就是已经越界。

如果觉得上述代码难以理解,则可用下列方式来实现

       打印的思路不变!新思路:将问题转换为数字排列的问题;我们发现如果在数字的前面补0,就会发现 n位所有十进制数其实就是 n个从 0到 9的全排列。也就是说,我们把数字的每一位都从 0到 9排列一遍,就得到了所有的十进制数。

void PrintNumber(char *number) //打印字符串表示的数字
{
	int len = strlen(number);
	bool isBeginning0 = true; 
	for (int i = 0; i < len; i++)
	{
		if (isBeginning0 && number[i] != '0') //找到第一个不为'0'的字符
			isBeginning0 = false;

		if (!isBeginning0) //打印
			printf("%c", number[i]);
	}
	printf("\t");
}

void PrintToMaxOfNDigitsRecursively(char *number, int length, int index)
{
	if (index == length - 1) //当index==length-1说明到了数的最后一位
	{
		PrintNumber(number);
		return;
	}

	for (int i = 0; i < 10; ++i)
	{
		number[index + 1] = '0' + i; 
		PrintToMaxOfNDigitsRecursively(number, length, index + 1); //递归实现
	}
}

void PrintToMaxOfNDigits(int n)
{
	if (n <= 0)
		return;

	char *number = new char[n + 1]; //申请n+1个位置
	number[n] = '\0';

	for (int i = 0; i < 10; i++)
	{
		number[0] = i + '0'; //0下标可取值为 0~9
		PrintToMaxOfNDigitsRecursively(number, n, 0); //从0下标开始
	}

	delete[] number;
}

int main()
{
	int n;
	cin >> n;
	cout << endl;
	PrintToMaxOfNDigits(n);
}

代码的过程如下:

 而当n特别大时不适宜用递归,层次太深,可能会导致栈溢出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值