算法-数位递增的数

本文介绍了一种初级算法题——数位递增数的判断及计数问题。通过两种不同的实现方式对比,探讨了使用字符串处理和数学计算处理整型数的不同性能表现,并给出了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题概述


题目:算法-数位递增的数-

问题描述:
一个正整数如果任何一个数位不大于右边相邻的数位,则称为一个数位递增的数,
例如1135是一个数位递增的数,而1024不是一个数位递增的数.
给定正整数 n,请问在整数 1 至 n 中有多少个数位递增的数?

输入:
输入的第一行包含一个整数 n。
输出:
输出一行包含一个整数,表示答案。
样例输入:
30
样例输出:
26


1. 问题分析


1.1 算法核心

这是一道初级算法题,较为容易,算法核心点在于对于整形数的认识,快速解出题目是我们的终极目标;


1.2 算法分析

虽然这是一道初级算法题,但是有很多进阶的算法题类似整形数问题都是由此演变而来的,注重底层细节是我们写算法的程序猿必不可少的素质;
而本题最重要的底层细节就是整形数的处理,一般我们会想到利用C++的STL容器string字符串类对象去解决,即下面解决方案的第一个声明函数checkIncreaseByString,从字符串尾开始向前比较,只要有一个不符合递增规律就说明不满足题意直接返回false即可,表示这个整形数不是数位递增的数,如果正常退出比较循环说明这是一个数位递增的数,返回true表示,然后相应的让我们预先定义的计数器+1就行,最后按照题目要求输出即可;
需要注意的点就是这类整形数题目有多种解决方法,可以利用字符串string类对象的函数,也可以利用数学计算的特点,比如求余运算等对整形数做处理。针对这两种方法呢,我进行了性能测试,最后发现利用string类对象的方法性能较差,性能上两者至少差了10倍,这也启示我们类虽然解决了代码复用的问题,封装了很多函数属性等,给我们程序猿带来很大便利,但很多优秀的算法的核心其实是依靠的程序猿的思维过程,一个程序猿很熟悉底层细节,那么他就能设计出一个针对某个问题效率很高的算法,这一点是母庸质疑的!


2. 解决方案

#include <iostream>
#include <string>
using namespace std;

bool checkIncreaseByString(int t);
bool checkIncreaseByInteger(int t);

int main()
{
	int ans = 0, n;

	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
		//if(checkIncreaseByString(i)) ans++;
		if(checkIncreaseByInteger(i)) ans++;
	printf("%d\n", ans);

	return 0;
}

bool checkIncreaseByString(int t)
{
	char temp[8];

	sprintf(temp, "%d", t);
	string s(temp);
	for(int i = 1; i < s.size(); i++)
		if(s[i - 1] > s[i]) return false;

	return true;
}

bool checkIncreaseByInteger(int t)
{
	int t1 = t % 10;
	int t2;

	t /= 10;
	while(t)
	{
		t2 = t % 10;
		if(t1 < t2)
			return false;
		t1 = t2;
		t /= 10;
	}

	return true;
}

3. 资源分享

旗木白哉のGitHub:
https://github.com/YangMengHeng
C++ / C源代码下载地址:
https://github.com/YangMengHeng/myCode/tree/master/VSCode
Java源代码下载地址:
https://github.com/YangMengHeng/myCode/tree/master/Java
旗木白哉のblog源代码下载地址:
https://github.com/YangMengHeng/myCode/tree/master/%E6%97%97%E6%9C%A8%E7%99%BD%E5%93%89%E3%81%AEblog


### 数位递增的概念 数位递增是指一个正整数中的每一位字都不大于其右侧相邻的字。换句话说,对于任意位置 \(i\) 的字 \(d_i\) 和其右侧相邻的位置 \(j\) 的字 \(d_j\) (其中 \(i < j\)),满足条件 \(d_i \leq d_j\)。例如,1135 是一个数递增,因为它的每一位都小于等于下一位;而 1024 不是数位递增,因为在第二位上出现了较大的字。 这种定义可以通过逐位比较来验证一个数是否属于数位递增类别[^3]。 --- ### 实现方法与算法 #### 方法一:字符串处理方式 通过将字转换为字符串形式逐一比较字符大小,可以判断该是否为数位递增。以下是 Python 中的一种实现: ```python def count_increasing_numbers(n): ans = 0 for number in range(1, n + 1): # 遍历从 1 到 n 的所有字 s = str(number) # 将当前字转为字符串 flag = True # 假设当前字符合条件 for j in range(1, len(s)): if s[j - 1] > s[j]: # 如果前一位大于后一位,则不符合条件 flag = False break # 提前退出循环 if flag: # 若标志仍为真,则计加一 ans += 1 return ans # 返回最终的结果 if __name__ == "__main__": n = int(input()) result = count_increasing_numbers(n) print(result) ``` 这种方法的时间复杂度主要取决于输入范围内的遍历次以及每次内部的逐位对比操作,总体时间复杂度大约为 O(k * log₁₀n),其中 k 表示总共有个数需要被检测,log₁₀n 表示平均每个数所含有的位--- #### 方法二:动态规划优化 另一种更高效的解决方案涉及动态规划的思想。我们可以预先计算出不同长度下的可能组合目,并利用这些预计算结果快速得出答案。具体来说,假设 dp[l][k] 表示长度为 l 并且最高位不超过 k 的有效数位递增量,则状态转移方程如下所示: \[ dp[l][k] = \sum_{m=0}^{k}{dp[l-1][m]} \] 初始条件设置为当长度为 1 时,任何单个数字都是有效的数位递增。因此有: \[ dp[1][k] = k+1,\quad 对于所有的 k \in [0..9] \] 最后统计范围内所有合法值即可得到总目。此方法能够显著减少重复运算量,在大规模据集上的表现优于简单枚举法[^1]。 --- #### Java 实现最长连续递增序列 虽然题目询问的是关于数位递增的具体概念及其应用,但这里也提供一段基于组寻找最长严格单调上升子串的代码作为补充说明,这有助于理解如何高效地判定一系列元素之间的相对顺序关系: ```java class Solution { public int findLengthOfLCIS(int[] nums) { if (nums.length == 0) return 0; int max = 0; int num = 1; for (int i = 0; i < nums.length - 1; i++) { if (nums[i] < nums[i + 1]) { num++; } else { if (max < num) max = num; num = 1; } } return Math.max(num, max); } } ``` 上述代码片段展示了如何在一个整型组中找到最长连续递增序列的长度。尽管它并不直接解决原问题,但它体现了类似的逻辑思维过程——即通过对前后项之间差值或者大小关系进行评估从而完成特定目标的任务[^2]。 --- ### 总结 综上所述,无论是采用简单的暴力枚举还是复杂的动态规划策略都可以有效地解决问题。选择哪种技术路线应视具体情况而定,比如性能需求、开发周期等因素都会影响决策方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值