• LeetCode 第7题:整数反转(Python3解法)


    1:问题描述

    来源:LeetCode

    难度:中等


    问题详情:
    给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

    如果反转后整数超过 32 位的有符号整数的范围 [ − 2 31 , 2 31 − 1 ] [−2^{31},2^{31} − 1] [2312311] ,就返回 0

    假设环境不允许存储 64 位整数(有符号或无符号)。


    2:问题分析

    2.1 时间复杂度和空间复杂度

    在真正开始介绍各种算法前,先以表格形式展示各自的时间复杂度和空间复杂度:

    算法时间复杂度空间复杂度
    列表法 O ( l o g ( x ) ) O(log(x)) O(log(x)) O ( l o g ( x ) ) O(log(x)) O(log(x))
    数学解析法O( l o g ( x ) log(x) log(x)) O ( 1 ) O(1) O(1)

    2.2 列表法

    题目本身强调了不允许使用64位的类型,因此当反转后数值超过 32位数能够表达的 [ − 2 31 , 2 31 − 1 ] [−2^{31},2^{31} − 1] [2312311] 范围,也就无法直接使用int类型表达。这是比较麻烦的一点,否则可以直接使用转为字符串类型,然后再反向切片然后转为int即可。

    1. 使用列表从低位到高位依次存储x中的每一位的数值。
    2. 然后使用同样的操作存储界限值
    3. 首先比较两者的长度,如果x对应的反转列表长度较短,直接转换回int类型
    4. 如果两者长度相等,则从高位到低位依次对比数值大小,如果x对应的数较小,则转换为int类型返回;如果x对应的数较大,说明数值溢出了,返回0.

    2.2.1 代码

    def reverse(x: int) -> int:
        sign = 1
        if x < 0:
            sign = -1
    
        def get_list(x):
            x_list = []
            while x:
                x, t = divmod(x, 10) # 与 x = x // 10, t= x%10等价
                x_list.append(t)
            return x_list
    
        def get_int(x_list):
            x = 0
            for i, item in enumerate(x_list):
                x += item * 10 ** (len(x_list) - i - 1)
    
            return x
    
        x = abs(x)
        x_list = get_list(x)  # 反转后的数值列表,因为转换为列表时出来的就是反的
        if sign == 1:
            top_list = get_list(2 ** 31)[::-1]  # 正常的边界值 + 1
        else:
            top_list = get_list(2 ** 31 + 1)[::-1]
    
        # 如果长度没有上限的大,那么不用担心这个反转后的数值超过边界值
        if len(x_list) < len(top_list):
            return get_int(x_list) * sign
        # 如果长度相等,那么就需要考虑是否超过界限:
        # 如果数值相等,那么说明刚好超过界限,直接返回0
        # 如果数值不等,那么就需要依次比较高位的数,如果有反转后的数值有高位大于界限,那么返回0
        # 如果高位小于界限,那么直接返回反转后的数值×符号位
        # 如果高位相等,则比较后边的高位值
        elif len(x_list) == len(top_list):
            if x_list == top_list:
                return 0
            for m, n in zip(x_list, top_list):
                if m > n:
                    return 0
                elif m < n:
                    return get_int(x_list) * sign
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    对于时间复杂度,因为int类型的数值长度 l = i n t ( log ⁡ 10 ( x ) ) + 1 l=int(\log _{10}\left( x\right))+1 l=int(log10(x))+1计算得到,所以列表创建和遍历的时间复杂度为 O ( l o g ( x ) ) O(log(x)) O(log(x)),因此该算法的时间复杂度为 O ( l o g ( x ) ) O(log(x)) O(log(x))

    空间复杂度同样为 O ( l o g ( x ) ) O(log(x)) O(log(x))

    2.3 数学解析法

    虽然上述算法通过列表,避免了直接生成可能溢出的结果值,但是列表法的空间复杂度较高。

    在介绍数学解析法之前,我们先考虑一个正数的不会溢出的情况。
    假设 x = 123 x=123 x=123,那么反转的流程则是:

    1. 123 / / 10 = 12 , 123 % 10 = 3 123 // 10 = 12, 123 \% 10 = 3 123//10=12,123%10=3
    2. 0 ∗ 10 + 3 = 3 0*10+3=3 010+3=3
    3. 12 / / 10 = 1 , 12 % 10 = 2 12//10=1, 12 \% 10=2 12//10=1,12%10=2
    4. 3 ∗ 10 + 2 = 32 3 * 10 + 2 = 32 310+2=32
    5. 1 / / 10 = 0 , 1 % 10 = 1 1//10=0,1\%10=1 1//10=0,1%10=1
    6. 32 ∗ 10 + 1 = 321 32 * 10 + 1=321 3210+1=321

    代码如下所示:

    rev = 0
    while x:
        x, t = divmod(x, 10) # 上述流程中的1、3、5步骤
        rev = rev * 10 + t #上述流程中的2、4、6步骤
    
    • 1
    • 2
    • 3
    • 4

    同样以正数为例:
    如果要保证反转后的数值不溢出,那么就需要最后一次执行的 r e v ∗ 10 + t < = I N T _ M A X rev * 10 + t<= INT\_MAX rev10+t<=INT_MAX,这里 I N T _ M A X = 2 31 − 1 INT\_MAX=2^{31}-1 INT_MAX=2311.

    我们将 I N T _ M A X INT\_MAX INT_MAX转换为如下形式:
    I N T _ M A X = ⌊ I N T _ M A X 10 ⌋ × 10 + 7 INT\_MAX=\lfloor \dfrac{INT\_MAX}{10}\rfloor ×10 + 7 INT_MAX=10INT_MAX×10+7

    那么需要:
    r e v ∗ 10 + t < = ⌊ I N T _ M A X 10 ⌋ × 10 + 7 rev * 10 + t<= \lfloor \dfrac{INT\_MAX}{10}\rfloor ×10 + 7 rev10+t<=10INT_MAX×10+7
    经过简单的变换后:
    ( r e v − ⌊ I N T _ M A X 10 ⌋ ) ∗ 10 < = 7 − t (rev-\lfloor \dfrac{INT\_MAX}{10}\rfloor) * 10 <= 7-t rev10INT_MAX10<=7t

    • r e v > ⌊ I N T _ M A X 10 ⌋ rev>\lfloor \dfrac{INT\_MAX}{10}\rfloor rev>10INT_MAX时,左边的数值 > = 10 >=10 >=10,而右边的 0 = < t < = 9 0=0=<t<=9,因此 7 − t < 10 7-t<10 7t<10,所以这个不等式是不成立的;
    • r e v = ⌊ I N T _ M A X 10 ⌋ rev=\lfloor \dfrac{INT\_MAX}{10}\rfloor rev=10INT_MAX时,左边的数值 = 0 =0 =0,而右边的 0 = < t < = 7 0=0=<t<=7时不等式成立,同时可以考虑下, r e v = ⌊ I N T _ M A X 10 ⌋ rev=\lfloor \dfrac{INT\_MAX}{10}\rfloor rev=10INT_MAX,这个 x x x的长度应该和 I N T _ M A X INT\_MAX INT_MAX是相同的,而最后一次的 t t t则是 x x x的最高位,根据题意其是 t = 1 , 2 t=1,2 t=1,2的,所以这个等式是必然成立的。
    • r e v < ⌊ I N T _ M A X 10 ⌋ rev<\lfloor \dfrac{INT\_MAX}{10}\rfloor rev<10INT_MAX时,左边的数值 < = − 10 <=-10 <=10,而右边的 0 = < t < = 9 0=0=<t<=9,因此 − 2 = < 7 − t < = 7 -2=<7-t<=7 2=<7t<=7,所以这个不等式是成立的;

    因此通过以上的分析,当 r e v < = ⌊ I N T _ M A X 10 ⌋ rev<=\lfloor \dfrac{INT\_MAX}{10}\rfloor rev<=10INT_MAX时,反转后的数值不会溢出;反之,当 r e v > ⌊ I N T _ M A X 10 ⌋ rev>\lfloor \dfrac{INT\_MAX}{10}\rfloor rev>10INT_MAX时,反转后的数值会溢出。

    2.3.1 代码

    因为 I N T _ M A X = 2 31 − 1 INT\_MAX = 2 ^{31} -1 INT_MAX=2311, 而 x x x的下限 I N T _ M I N = − 2 31 INT\_MIN=-2^{31} INT_MIN=231.
    ⌊ I N T _ M A X 10 ⌋ = ⌊ ∣ I N T _ M I N ∣ 10 ⌋ \lfloor \dfrac{INT\_MAX}{10}\rfloor=\lfloor \dfrac{|INT\_MIN|}{10}\rfloor 10INT_MAX=10INT_MIN,所以我们直接使用 r e v > ⌊ 2 31 10 ⌋ rev>\lfloor \dfrac{2^{31}}{10}\rfloor rev>10231作为数值溢出的判断条件。

    同时首先得到符号值,然后取绝对值,将正数和负数的情况统一为正数情况处理。

    def reverse2(x: int) -> int:
        rev = 0
        sign = 1
        max_rev = 2 ** 31 // 10
        if x < 0:
            sign = -1
        x = abs(x)
        while x:
            if rev > max_rev:
                return 0
            x, t = divmod(x, 10)
            rev = rev * 10 + t
        return rev * sign
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    时间复杂度同样是 O ( l o g ( x ) ) O(log(x)) O(log(x)),而空间复杂度则只是 O ( 1 ) O(1) O(1)级别的。

  • 相关阅读:
    脑电连通性:优化研究设计和评估的基本指南和检查清单
    【C语言学习笔记---学指针必刷它】
    oracle 12c GI卸载流程
    [附源码]计算机毕业设计大学生心理健康测评系统
    Java学习之this关键字
    Linux内存管理(1):memblock
    [python知识巩固]内建函数reversed()
    PwnTheBox 刷题记录crypto篇
    业务及系统架构对发布的影响
    《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(17)-Fiddler如何充当第三者,再识AutoResponder标签-下篇
  • 原文地址:https://blog.csdn.net/weixin_43490422/article/details/126545270