【每日一题】LeetCode - 反转整数问题

题目描述

给你一个 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 位整数)。

分析步骤

  1. 反转数字的基本思路

    • 我们可以通过不断对 x 取余数,然后去掉个位数,从而得到 x 的反转。
    • 具体来说,使用 x % 10 获取 x 的最后一位数字,将其加入结果 ans,再用 x /= 10 去掉最后一位,重复该过程直到 x 为 0。
  2. 处理溢出问题

    • 由于题目要求的整数是 32 位的有符号整数,范围在 [-2³¹, 2³¹ - 1] 之间,因此反转时需要检查是否溢出。
    • 如果在将新的数字加入结果前,结果 ans 已经超过了 MAX_INT/10(因为后面还要再乘以 10),那么此时再加入新的数字一定会溢出,需要直接返回 0。
    • 同理,对于负数的处理也是一样。
  3. 处理负数

    • 对于负数的情况,反转的逻辑和正数是相同的,区别只是符号不同,因此在处理时我们可以直接用整数反转的方式计算。
  4. 最终的判断

    • 完成反转后,再次检查结果是否在合法范围内,如果超出范围则返回 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;
    }
};

代码解释

  1. int ans = 0;:

    • 初始化反转后的结果为 0。
  2. while(x):

    • x 不为 0 时,循环继续,处理每一位数字。
  3. if(abs(ans) > ~(1 << 31) / 10) return 0;:

    • 在反转过程中,检查当前结果 ans 是否会导致溢出。如果 ans 已经大于 MAX_INT / 10,则再继续操作一定会超出 32 位整数范围,因此立即返回 0。
  4. int n = x % 10;:

    • 取出 x 的最后一位数字。
  5. x /= 10;:

    • x 去掉最后一位。
  6. ans = ans * 10 + n;:

    • 更新反转后的结果,将新取出的最后一位数字加到结果的末尾。

位运算补充

在代码中,表达式 ~(1 << 31) 是用于表示32位有符号整数的最大值,即 2147483647(即 2³¹ - 1)。下面详细解释这一部分:

  1. 1 << 31

    • 1 是一个二进制数,只有最低位是1,其他位都是0。
    • << 是左移操作,将数字的二进制表示向左移动指定的位数。
    • 1 << 311 向左移动31位,结果是 10000000 00000000 00000000 00000000,即表示 2³¹,这个数是 2147483648。这是32位有符号整数的最小负数(也就是 -2³¹)。
  2. ~(1 << 31)

    • ~ 是按位取反运算符,它会将每一位的二进制数都取反。对于 1 << 31,取反后会得到 01111111 11111111 11111111 11111111,即 2147483647,也就是 2³¹ - 1,这就是32位有符号整数的最大正数。
  3. 总结

    • 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 进行逐位反转操作。为了更好地理解其时间复杂度,我们可以考虑以下几点:

  1. 逐位处理数字

    • 假设输入整数 xd 位数字。我们在每次循环中通过 x % 10 获取最后一位数字,然后通过 x /= 10 去掉这最后一位。
    • 每次循环处理一位数字,因此循环的次数与 x 的位数 d 成正比。
  2. x 的位数

    • 一个整数的位数 d 与其大小 x 有以下关系:
      • 对于十进制数来说,位数 d 满足:d = ⌊log₁₀(x)⌋ + 1,即 x 的位数是 x 的对数值(以10为底)的整数部分再加1。
    • 比如,x = 1234 有 4 位数字,因为 log₁₀(1234) ≈ 3.09,取整数部分后加 1 即可得到 4。
  3. 计算时间复杂度

    • 在每次循环中,我们执行常数时间的操作,例如取模 % 和除法 /,因此每次迭代的时间复杂度是 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

总结

通过这道题,我们掌握了如何在限制空间的情况下反转整数,同时学会了如何处理整数溢出的情况。这类问题在编程面试中较为常见,重点在于对细节的把握,特别是在有限的空间下进行数字操作时,溢出问题是不可忽视的。

相关推荐
南宫生18 分钟前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_30 分钟前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子42 分钟前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
冷眼看人间恩怨1 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡1 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客1 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin1 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码1 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
scan7241 小时前
LILAC采样算法
人工智能·算法·机器学习
菌菌的快乐生活2 小时前
理解支持向量机
算法·机器学习·支持向量机