题目描述
给你一个 32 位的有符号整数 x
,要求返回将 x
中的数字部分反转后的结果。
但是需要注意以下几点:
- 如果反转后整数超过 32 位有符号整数的范围 [−2³¹, 2³¹ − 1],那么返回 0。
- 假设环境不允许存储 64 位整数(有符号或无符号)。
示例
-
示例 1:
- 输入:
x = 123
- 输出:
321
- 输入:
-
示例 2:
- 输入:
x = -123
- 输出:
-321
- 输入:
-
示例 3:
- 输入:
x = 120
- 输出:
21
- 输入:
-
示例 4:
- 输入:
x = 0
- 输出:
0
- 输入:
解题思路
该题的关键点在于如何反转整数并处理好溢出情况。我们需要仔细控制反转过程中整数的范围,同时确保算法高效且不会使用超过题目要求的空间(不能使用 64 位整数)。
分析步骤
-
反转数字的基本思路:
- 我们可以通过不断对
x
取余数,然后去掉个位数,从而得到x
的反转。 - 具体来说,使用
x % 10
获取x
的最后一位数字,将其加入结果ans
,再用x /= 10
去掉最后一位,重复该过程直到x
为 0。
- 我们可以通过不断对
-
处理溢出问题:
- 由于题目要求的整数是 32 位的有符号整数,范围在 [-2³¹, 2³¹ - 1] 之间,因此反转时需要检查是否溢出。
- 如果在将新的数字加入结果前,结果
ans
已经超过了MAX_INT/10
(因为后面还要再乘以 10),那么此时再加入新的数字一定会溢出,需要直接返回 0。 - 同理,对于负数的处理也是一样。
-
处理负数:
- 对于负数的情况,反转的逻辑和正数是相同的,区别只是符号不同,因此在处理时我们可以直接用整数反转的方式计算。
-
最终的判断:
- 完成反转后,再次检查结果是否在合法范围内,如果超出范围则返回 0,否则返回反转后的结果。
代码实现
cpp
class Solution {
public:
int reverse(int x) {
int ans = 0; // 用于存储反转后的结果
while(x) {
// 检查 ans 是否溢出,如果 ans 在反转之前已经大于 MAX_INT / 10 则直接返回 0
if(abs(ans) > ~(1 << 31) / 10) return 0;
// 取出 x 的最后一位数字
int n = x % 10;
// 去掉 x 的最后一位数字
x /= 10;
// 更新反转后的结果
ans = ans * 10 + n;
}
return ans;
}
};
代码解释
-
int ans = 0;
:- 初始化反转后的结果为 0。
-
while(x)
:- 当
x
不为 0 时,循环继续,处理每一位数字。
- 当
-
if(abs(ans) > ~(1 << 31) / 10) return 0;
:- 在反转过程中,检查当前结果
ans
是否会导致溢出。如果ans
已经大于MAX_INT / 10
,则再继续操作一定会超出 32 位整数范围,因此立即返回 0。
- 在反转过程中,检查当前结果
-
int n = x % 10;
:- 取出
x
的最后一位数字。
- 取出
-
x /= 10;
:- 将
x
去掉最后一位。
- 将
-
ans = ans * 10 + n;
:- 更新反转后的结果,将新取出的最后一位数字加到结果的末尾。
位运算补充
在代码中,表达式 ~(1 << 31)
是用于表示32位有符号整数的最大值,即 2147483647
(即 2³¹ - 1
)。下面详细解释这一部分:
-
1 << 31
:1
是一个二进制数,只有最低位是1,其他位都是0。<<
是左移操作,将数字的二进制表示向左移动指定的位数。1 << 31
将1
向左移动31位,结果是10000000 00000000 00000000 00000000
,即表示2³¹
,这个数是2147483648
。这是32位有符号整数的最小负数(也就是-2³¹
)。
-
~(1 << 31)
:~
是按位取反运算符,它会将每一位的二进制数都取反。对于1 << 31
,取反后会得到01111111 11111111 11111111 11111111
,即2147483647
,也就是2³¹ - 1
,这就是32位有符号整数的最大正数。
-
总结:
1 << 31
得到的是最小的32位有符号整数,即-2147483648
。~(1 << 31)
得到的是最大的32位有符号整数,即2147483647
。
因此,在代码中使用 ~(1 << 31)
代替直接写出 2147483647
,是为了用更底层的位操作来表示最大32位整数。
对于初学者,简单来说,~(1 << 31)
相当于 2147483647
,它表示的是 32 位有符号整数的最大值。
注意事项
- 在判断溢出时使用
MAX_INT / 10
是因为在下一次操作中我们会将当前的ans
乘以 10,如果当前ans
已经大于MAX_INT / 10
,那么下一步操作将导致溢出。 - 题目中假设环境不允许使用 64 位整数,因此在代码中必须保证使用的变量始终在 32 位整数的范围内进行操作。
复杂度分析
时间复杂度
在这个问题中,我们需要对输入的整数 x
进行逐位反转操作。为了更好地理解其时间复杂度,我们可以考虑以下几点:
-
逐位处理数字:
- 假设输入整数
x
有d
位数字。我们在每次循环中通过x % 10
获取最后一位数字,然后通过x /= 10
去掉这最后一位。 - 每次循环处理一位数字,因此循环的次数与
x
的位数d
成正比。
- 假设输入整数
-
x
的位数:- 一个整数的位数
d
与其大小x
有以下关系:- 对于十进制数来说,位数
d
满足:d = ⌊log₁₀(x)⌋ + 1
,即x
的位数是x
的对数值(以10为底)的整数部分再加1。
- 对于十进制数来说,位数
- 比如,
x = 1234
有 4 位数字,因为log₁₀(1234) ≈ 3.09
,取整数部分后加 1 即可得到 4。
- 一个整数的位数
-
计算时间复杂度:
- 在每次循环中,我们执行常数时间的操作,例如取模
%
和除法/
,因此每次迭代的时间复杂度是 O(1)。 - 总共要处理
d
次循环,而d ≈ log₁₀(x)
,所以整体时间复杂度为O(log₁₀(x))
。
- 在每次循环中,我们执行常数时间的操作,例如取模
具体推导过程
- 输入整数
x
的位数为d = ⌊log₁₀(x)⌋ + 1
。 - 每次操作中,我们执行常数级别的操作,共执行
d
次。 - 综上,时间复杂度为
O(log₁₀(x))
,其中x
是输入的整数。
因此,时间复杂度主要取决于整数 x
的位数大小,而位数与 log₁₀(x)
成正比,因此时间复杂度为 O(log₁₀(x))
。
空间复杂度
- O(1),只使用了常数空间存储
ans
和中间变量n
。
总结
通过这道题,我们掌握了如何在限制空间的情况下反转整数,同时学会了如何处理整数溢出的情况。这类问题在编程面试中较为常见,重点在于对细节的把握,特别是在有限的空间下进行数字操作时,溢出问题是不可忽视的。