1.题目
题目地址(258. 各位相加 - 力扣(LeetCode))
https://leetcode.cn/problems/add-digits/?envType=study-plan-v2&envId=primers-list
题目描述
给定一个非负整数 num
,反复将各个位上的数字相加,直到结果为一位数。返回这个结果。
示例 1:
输入: num =38
输出: 2 解释: 各位相加的过程为: 38 --> 3 + 8 --> 11 11 --> 1 + 1 --> 2 由于2
是一位数,所以返回 2。
示例 2:
输入: num = 0 输出: 0
提示:
0 <= num <= 231 - 1
进阶:你可以不使用循环或者递归,在 O(1)
时间复杂度内解决这个问题吗?
2. 题解
这道题的本质是计算自然数num的数根。
数根又称数字根(Digital root),是自然数的一种性质,每个自然数都有一个数根。对
于给定的目然数,反复将各个位上的数字相加,直到结果为一位数,则该一位数即为原自然数的数根。
计算数根的最直观的方法是模拟计算各位相加的过程,直到剩下的数字是一位数。利用自然数的性质,则能在\(O(1)\)的时间内计算数根。
2.1 模拟
思路
就是拆分,求和,再拆分,再求和; 直到只剩下个位数
代码
- 语言支持:C++
C++ Code:
class Solution {
public:
int addDigits(int num) {
while(num >= 10){
int sum = 0;
while(num > 0){
sum += num % 10;
num /= 10;
}
num = sum;
}
return num;
}
};
复杂度分析
令 n 为数组长度。
- 时间复杂度:\(O(lognum)\)
- 空间复杂度:\(O(1)\)
2.2 数学方法(拆分-补缺)
思路
假设整数num的十进制表示有\(n\)位,从最低位到最高位依次是\(a_0\)到\(a_{n-1}\)
则 num 可以写成如下形式(每一位都分出来个1, 用于构成树根):
\(\begin{aligned} num& =\sum_{i=0}^{n-1}a_i\times10^i \\ &=\sum_{i=0}^{n-1}a_i\times(10^i-1+1) \\ &=\sum_{i=0}^{n-1}a_i\times(10^i-1)+\sum_{i=0}^{n-1}a_i \end{aligned}\)
当\(i=0\)时,\(10^i-1=0\)是 9 的倍数;
当\(i\)是正整数时,\(10^i-1\)是由\(i\)位9 组成的整数,也是 9 的倍数。
因此对于任意非负整数\(i,10^i-1\)都是 9 的倍数。由此可得num与其各位相加的结果模 9 同余。
重复计算各位相加的结果果直到结果为一位数时,该一位数即为num的数根,num与其数根模 9 同余。
我们对num分类讨论:
num不是 9 的倍数时,其数根即为num除以 9 的余数。
num是 9 的倍数时:
\(\circ\)如果\(num=0\),则其数根是0;
\(\circ\)如果\(num>0\) ,则各位相加的结果大于0,其数根也大于0,因此其数根是 9。
细节
根据上述分析可知,当\(num>0\)时,其数根的结果在范围[1,9] 内,因此可以想到计算\(num-1\)除以 9 的余数然后加 1。
由于当\(num>0\)时,\(num-1\geq0\) ,非负数除以 9 的余数一定也是非负数,因此计算\(num-1\)除以 9 的余数然后加 1 的结果是正确的。
当\(num=0\)时,\(num-1=-1<0\) ,负数对 9 取余或取模的结果的正负在不同语言中有所不同。
\(\circ\)对于取余的语言,结果的正负和左操作数相同,则\(num-1\)对 9 取余的结果为-1 ,加 1 后得到结果0,可以得到正确的结果;
\(\circ\)对于取模的语言,结果的正负和右操作数相同,则\(num-1\)对 9 取模的结果的果为 8,加 1 后得到结果 9,无法得到正确的结果,此时需要对\(num=0\)的情况专门做处理。
代码
class Solution {
public:
int addDigits(int num) {
return (num - 1) % 9 + 1;
}
};
标签:10,结果,相加,数根,力扣,258,num,一位数
From: https://www.cnblogs.com/trmbh12/p/18165756