1:问题描述
来源:LeetCode
难度:简单
问题详情:
给你一个整数 x
,如果 x
是一个回文整数,返回 true
;否则,返回 false
。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
- 例如,121 是回文,而 123 不是
输入:x = 121
输出:true
输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数
进阶:你能不将整数转为字符串来解决这个问题吗?
2:问题分析
从给出的示例可以看出:
- 负数不是回文数
- 位数大于1且末尾为0的数不是回文数
同时,也可以想到:
- 只有1位的数是回文的
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) |
折半法 | O( l o g ( x ) log(x) log(x)) | O ( 1 ) O(1) O(1) |
2.2 字符串法
2.2.1 代码
根据进阶要求的限制,我们反而能够很容易想到这个解法。将数字转换为字符串列表,然后反转列表,再转换为数字对比是否与反转前相等即可。
同时将分析得到的三种情况先做一个返回。
def isPalindrome(x: int) -> bool:
"""不满足进阶要求"""
if x < 0:
return False
if 0 <= x <= 9:
return True
if x % 10 == 0:
return False
rev_x = int(''.join(list(str(x))[::-1]))
if rev_x == x:
return True
else:
return False
由于数字长度可以用 i n t ( l o g 10 ( x ) ) + 1 int(log10(x))+1 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))
2.3 直接法
2.3.1 代码
不使用字符串的前提下,我们就需要真的将数值反转过来对比。
def isPalindrome2(x: int) -> bool:
"""数值计算转换"""
if x < 0:
return False
if 0 <= x <= 9:
return True
if x % 10 == 0:
return False
rev_x = 0
s = x
while s:
s, t = divmod(s, 10)
rev_x = rev_x * 10 + t
if rev_x == x:
return True
else:
return False
由于数字长度可以用 i n t ( l o g 10 ( x ) ) + 1 int(log10(x))+1 int(log10(x))+1计算得到,因此时间复杂度为 O ( l o g ( x ) ) O(log(x)) O(log(x));
对于空间复杂度,其为 O ( 1 ) O(1) O(1)
2.3 折半法
2.3.1 代码
折半法其实是直接法的优化,因为其实我们并不需要将所有的位数都反转,而只需反转一半,然后对比是否与另一半未反转的相同即可。
举例来说,1221
,我们只需反转后半部分的21
为12
,然后对比前半部分看是否一致。
def isPalindrome4(x: int) -> bool:
if x < 0:
return False
if 0 <= x <= 9:
return True
if x % 10 == 0:
return False
rev_x = 0
while x > rev_x:
x, t = divmod(x, 10)
rev_x = rev_x * 10 + t
return rev_x == x or rev_x // 10 == x
虽然思想上比较简单,但是,我们如何判断当前已经完成了一半的反转了呢?
对此,我们可以反过来想,当还未完成反转的时候有什么特点?
- 那就是前半部分的位数比后半部分的位数多,可以使用
前半部分>反转的后半部分
作为循环判断的依据。 - 但是在位数相同的情况下,也可以达到前半部分>后半部分这种情况。比如
3221
这个数,其在反转的过程中够实现这个特例,32>12
。
对此,我们只能让后半部分暂时占点便宜,让其再次循环多取走一位,变成3 < 122
这样就可以终止循环。
然后我们就前部分==后部分
或者前部分==后部分//10
作为返回值。可以判断前半部分3
和后半部分122
这两个判断条件都不满足,就是false
了。
前部分==后部分//10
这个条件是为了应对输入的数x
位数是奇数的情况,比如121。
按照while的流程:
前半部分 | 后半部分 |
---|---|
121 | 0 |
12 | 1 |
1 | 12 |
我们可以看出,前部分占了1位,后半部分占了2位,我们需要使用前部分==后部分//10
去除多后半部分出来的末位2
,去除后左右两边都为1
,返回true
.
由于数字长度可以用 i n t ( l o g 10 ( x ) ) + 1 int(log10(x))+1 int(log10(x))+1计算得到,因此时间复杂度为 O ( l o g ( x ) ) O(log(x)) O(log(x));
对于空间复杂度,其为 O ( 1 ) O(1) O(1)