深入理解回文数 逐层优化
题目
给你一个整数
x
,如果x
是一个回文整数,返回true
;否则,返回false
。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
提示:
-231 <= x <= 231 - 1
**进阶:**你能不将整数转为字符串来解决这个问题吗?
字符串法
回文数本身就是一个字符串的比较,转化为字符串做易理解。
class Solution:
def isPalindrome(self, x: int) -> bool:
return str(x) == str(x)[::-1]
python有黑魔法转字符串太容易了,一行解决属于是不讲武德,使用其他语言稍微麻烦一点。不符合题目进阶要求。
逐位比较
思路
将原数字一位一位取出来,再倒着算回去看是否等于原来的数字。负数一定不是回文数,提前做个判断。
如何将数字一位一位取出来呢?设位数为bit,个位是1,十位是2…可知:(x % (10**bit)) // (10**(bit-1))
,先取高一位的余数,去除高位的数字,再用该位整除,去掉低位的数字。
如何将取出来的数字还原呢?将取出的数放在一个数组中,将其在数组中的位置(索引)与位数关联起来(有序放置),乘对应的位数系数即可nums[i] * (10**(length-i-1))
。
那如何知道最高位是多少呢?当是最高位时,去除高位这一步算出来的数和原来的数是一样的,即取余这一步不会让数字发生改变,条件为:x == x % (10**length)
。
实现
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0: return False
nums, length = [], 0
while x != x % (10**length):
length += 1
temp = (x % (10**length))// (10**(length-1))
nums.append(temp)
num = 0
for i in range(len(nums)):
num += nums[i] * (10**(length-i-1))
return num == x
分析
该方法每位数字要遍历两遍(拆一遍,算一遍),时间复杂度为O(n),系数大致为2。由于使用了临时的nums数组存放每一位的数字,空间复杂度为O(n),比较低效。
回顾思路,导致低效的原因是不知道一共有多少位,在拆的时候必须拆完才知道何时停止,算反转数的时候又要用总位数为参数,导致两次循环。
引入对数计算总位数
思路
因为本题中数字是十进制的,则以10为底计算对数。直接算出的数为浮点数,使用int转换后向下取整了,需要加一length = int(log(x,10)) + 1
。需要注意的是,0没有对数,需要单独考虑。
还是一位一位拆,因为知道了总位数,可以边拆边算,少一次循环,而且不需要辅助数组了。在遍历完每一位后,比较两个数是否相等即可。
实现
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0: return False
elif x == 0: return True
from math import log
length = int(log(x,10))+1
num = 0
for i in range(length):
temp = (x % (10**(i+1))) // (10**i)
num += temp * (10**(length-i-1))
return num == x
分析
只用遍历一次了,时间复杂度系数小于2了。没有用额外与输入规模相关的内存空间,空间复杂度降为O(1)。
但判断回文数真的需要完整遍历吗?
折半比较
思路
回文数是正反看都一样的数,在上述循环中,因为我们知道了总长度,其实是可以头尾一起看,发现一个不一致就可以返回否了。计算尾端的表达式为:(x % (10**(length-i))) // (10**(length-i-1))
实现
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0: return False
elif x == 0: return True
from math import log
length = int(log(x,10))+1
for i in range(length//2 + 1):
rear = (x % (10**(i+1))) // (10**i)
pre = (x % (10**(length-i))) // (10**(length-i-1))
if rear != pre: return False
return True
分析
只用循环半次,虽然时间复杂度还是O(n),但系数已经降低到1/2左右了。