【LeetCode 每日一题】1317. 将整数转换为两个无零整数的和
Problem: 1317. 将整数转换为两个无零整数的和
文章目录
- 整体思路
- 完整代码
- 时空复杂度
- 时间复杂度:O(N * log N)
- 空间复杂度:O(1)
整体思路
这段代码的目的是找到两个正整数 A 和 B,使得它们满足以下两个条件:
- A + B = n (n 是给定的输入)
- A 和 B 的十进制表示中都不包含数字 ‘0’。
该算法采用了一种 优化的暴力搜索 (Optimized Brute-force Search) 策略。它通过系统地遍历所有可能的组合,直到找到第一个满足条件的解。
其核心逻辑步骤如下:
-
搜索策略:枚举其中一个数
- 算法的核心思想是,只要确定了第一个数
i
,第二个数rest
就唯一确定为n - i
。 - 因此,问题就简化为:寻找一个合适的
i
,使得i
和n - i
都不含零。
- 算法的核心思想是,只要确定了第一个数
-
优化搜索范围
- 代码通过一个
for
循环来枚举第一个数i
,for (int i = 1; i <= (n + 1) / 2; i++)
。 - 这个循环范围非常巧妙:
- 它从
1
开始,因为题目要求的是正整数。 - 它到
(n + 1) / 2
结束。这是为了避免重复检查。例如,如果n=102
,当i=11
时,会检查(11, 91)
。我们没有必要在后面让i
遍历到91
再去检查(91, 11)
。遍历到中点就足够覆盖所有无序的组合了。
- 它从
- 代码通过一个
-
验证条件
- 在每次循环中,对于当前的
i
和计算出的rest = n - i
,算法需要验证它们是否都是 “No-Zero Integers”。 - 这个验证工作由一个辅助函数
nonZeroInt(int n)
来完成。
- 在每次循环中,对于当前的
-
nonZeroInt
辅助函数逻辑- 这个函数通过一个
while
循环,逐位检查一个整数的每一位。 - 取余操作 (
n % 10
):获取数字n
的个位数。如果这个个位数是0
,则说明该数包含零,立即返回false
。 - 整除操作 (
n /= 10
):将数字n
的最后一位移除,以便在下一次循环中检查新的个位数(即原来的十位数)。 - 如果循环正常结束(即
n
变为0
),说明在整个过程中没有发现任何一位是零,函数返回true
。
- 这个函数通过一个
-
找到解并终止
- 主函数中的
if
语句调用nonZeroInt
两次,分别检查i
和rest
。 - 一旦找到第一个同时满足条件的
(i, rest)
对,就将其存入结果数组ans
,并立即使用break
语句跳出循环。 - 因为
i
是从小到大遍历的,所以找到的第一个解一定是i
最小的那个解。
- 主函数中的
完整代码
class Solution {/*** 找到两个不含零的正整数,它们的和为 n。* @param n 目标和* @return 一个包含两个不含零整数的数组*/public int[] getNoZeroIntegers(int n) {// ans: 用于存储最终结果的数组int[] ans = new int[2];// 步骤 1 & 2: 遍历第一个数 i 的可能性,范围经过优化以避免重复// i 从 1 开始,到 n 的一半左右结束for (int i = 1; i <= (n + 1) / 2; i++) {// 计算第二个数 restint rest = n - i;// 步骤 3: 验证 i 和 rest 是否都不含零if (nonZeroInt(i) && nonZeroInt(rest)) {// 如果都满足条件,则找到了一个解ans[0] = i;ans[1] = rest;// 步骤 5: 找到第一个解后立即终止循环break;}}return ans;}/*** 辅助函数:检查一个整数的十进制表示中是否包含数字 0。* @param n 要检查的整数* @return 如果不含 0 则返回 true,否则返回 false*/private boolean nonZeroInt(int n) {// 循环直到所有位都被检查完 (n 变为 0)while (n != 0) {// 使用取余运算获取 n 的个位数if (n % 10 == 0) {// 如果个位数是 0,则该数不满足条件return false;}// 使用整除运算移除 n 的个位数,以便下一轮检查n /= 10;}// 如果循环结束都没有发现 0,说明该数满足条件return true;}
}
时空复杂度
时间复杂度:O(N * log N)
- 外层循环:
for (int i = 1; i <= (n + 1) / 2; i++)
。这个循环的执行次数与输入n
成线性关系,大约执行N/2
次。因此,这部分是 O(N)。 - 内层操作 (
nonZeroInt
):- 在每次外层循环中,会调用
nonZeroInt
函数两次。 nonZeroInt(k)
的时间复杂度取决于整数k
的位数。一个整数k
的位数大约是log10(k)
。- 由于
i
和rest
的值都小于n
,所以nonZeroInt
函数的执行时间是 O(log N)。
- 在每次外层循环中,会调用
- 综合分析:
- 总的时间复杂度是 (外层循环次数) * (内层操作时间)。
- 即
O(N) * (O(log i) + O(log(n-i)))
,这可以简化为 O(N * log N)。
空间复杂度:O(1)
- 主要存储开销:
int[] ans = new int[2]
: 结果数组的大小是固定的2
,不随输入n
的大小变化。- 其他变量:
n
,i
,rest
都是基本类型的变量。
- 综合分析:
- 算法没有使用任何与输入规模
N
成比例的额外数据结构。 - 因此,其额外辅助空间复杂度为 O(1)。
- 算法没有使用任何与输入规模