1、整数反转(7)
题目描述:
题解一:利用字符串反转
- 整数—>字符串—>反转
- 注意反转后溢出范围的判断
class Solution:
def reverse(self, x: int) -> int:
if -10<x<10:
return x
str_x=str(x)
if str_x[0] != "-":
str_x=str_x[::-1]
x=int(str_x)
else:
str_x=str_x[:0:-1]
x=int(str_x)
x=-x
return x if -(1<<31) < x < ((1<<31)-1) else 0
# return x if -2147483648 < x < 2147483647 else 0 这个时间少点
- 由于计算机是基于二进制的,所以原理是二进制操作的左移
1<<31
比2**31
快一点
题解二:数学解法
- 求余得末尾数字,末尾数字依次乘10,相加
思路
-
以12345为例,先拿到5,再拿到4,之后是3,2,1,我们按这样的顺序就可以反向拼接处一个数字了,也就能达到 反转 的效果。怎么拿末尾数字呢?好办,用取模运算就可以了
-
1、将12345 % 10 得到5,之后将12345 / 10
2、将1234 % 10 得到4,再将1234 / 10
3、将123 % 10 得到3,再将123 / 10
4、将12 % 10 得到2,再将12 / 10
5、将1 % 10 得到1,再将1 / 10 -
正负数的判断,我们可以将x取绝对值命名为y,暂时忽略正负数的区别,然后作上述操作,循环的判断条件应该是while(y!=0),无论正数还是负数,按照上面不断的/10这样的操作,最后都会变成0,所以判断终止条件就是
!=0
-
溢出范围的判断:
-
假设有1147483649这个数字,它是小于最大的32位整数2147483647的,但是将这个数字反转过来后就变成了9463847411,这就比最大的32位整数还要大了,这样的数字是没法存到int里面的,所以肯定要返回0(溢出了)。因此,每次反转后,我们可以判断其是否溢出,若溢出直接返回0.
-
那么反转后的数字最大是多少,才不会溢出呢?
-
由于最大数字是 2147483647,如果反转后数字大于 214748364那后面就不用再判断了,肯定溢出了;如果某个数字等于 214748364呢,这对应到下图中第三、第四、第五排的数字,需要要跟最大数的末尾数字比较,如果这个数字比7还大,说明溢出了。
-
class Solution:
def reverse(self, x: int) -> int:
y,res=abs(x),0
while y!=0:
#每次取末尾数字
tmp=y%10
if res>214748364 or (res==214748364 and tmp>7):
return 0
res=res*10+tmp
y//=10
return res if x>0 else -res
- 时间复杂度: O ( log n ) O(\log n) O(logn)
- 空间复杂度: O ( 1 ) O(1) O(1)。
2、字符串转换整数(8)
题目描述:
题解一: 正常遍历
- 根据转换规则,我们先去掉空格,然后判断是否有正负号,利用flag标记,最后开始匹配数字并将数字记录在ans里面,并结合flag返回值;
class Solution:
def myAtoi(self, s: str) -> int:
i=0
n=len(s)
while i<n and s[i]==' ':
i+=1
if n==0 or i==n:
return 0
flag=1
if s[i]=='-':
flag=-1
if s[i]=='+' or s[i]=='-':
i+=1
int_min=-1<<31
int_max=(1<<31)-1
ans=0
while i<n and '0'<=s[i]<='9':
ans=ans*10+int(s[i])
i+=1
if ans-1>int_max:
break
ans=ans*flag
if ans>int_max:
return int_max
return int_min if ans<int_min else ans
题解二:有限状态机
- 正常遍历的方法只是适用于分支讨论较少的情况,不然代码就会显得很冗余,也很难思考
- 而有限状态机的关键在于我们的程序在每个时刻有一个状态 s,每次从序列中输入一个字符 c,并根据字符 c 转移到下一个状态 s’。这样,我们只需要建立一个覆盖所有情况的从 s 与 c 映射到 s’ 的表格即可解决题目中的问题
- 这种方法这要确定好了状态就可以解决复杂的字符串问题,无论是在题目改成要求找多组数字或者是加入其他判断条件
下面是官方图给的状态
- 题目定义4状态,每种状态遇到空格,符号,数字、其他又会转入不同的状态
INT_MAX = (1 << 31) - 1
INT_MIN = -1 << 31
#定义自动机即有限状态机
class Automaton:
def __init__(self):
self.state = 'start'
self.sign = 1
self.ans = 0
self.table = {
'start': ['start', 'signed', 'in_number', 'end'],
'signed': ['end', 'end', 'in_number', 'end'],
'in_number': ['end', 'end', 'in_number', 'end'],
'end': ['end', 'end', 'end', 'end'],
}
#定义状态转移趋势
def get_col(self, c):
if c.isspace():
return 0
if c == '+' or c == '-':
return 1
if c.isdigit():
return 2
return 3
#定义状态
def get(self, c):
self.state = self.table[self.state][self.get_col(c)]
if self.state == 'in_number':
self.ans = self.ans * 10 + int(c)
self.ans = min(self.ans, INT_MAX) if self.sign == 1 else min(self.ans, -INT_MIN)
elif self.state == 'signed':
self.sign = 1 if c == '+' else -1
class Solution:
def myAtoi(self, s: str) -> int:
auto = Automaton()
for c in s:
auto.get(c)
return auto.sign * auto.ans
- 时间复杂度 O ( n ) O(n) O(n),
- 空间复杂度 O ( 1 ) O(1) O(1)
题解三:正则表达式
- 正则表达式通常被用来测试字符串内的模式、替换那些符合某个模式的文本、基于模式匹配从字符串中提取子字符串。
- 对于本题,这里我们如果应用正则表达式把数字那一串提取出来再进行处理就简单多了
- 正则表达式元字符
- python正则表达式中group
class Solution:
def myAtoi(self, s: str) -> int:
import re
mathes=re.match('[ ]*([+-]?\d+)',s)
if not mathes:
return 0
ans=int(mathes.group(1))
return min(max(ans,-1<<31),(1<<31)-1)
- 时间复杂度 O ( n ) O(n) O(n),
- 空间复杂度 O ( 1 ) O(1) O(1)
3、回文数(9)
题目描述:
题目链接
题解一:利用字符串做反转
- 整数—>字符串—>反转—>判断与之前的字符串是否一致
- 当x小于0时,肯定不是一个回文数,直接返回false
class Solution:
def isPalindrome(self, x: int) -> bool:
str_x=str(x)
if str_x[0]=="-":
return False
else:
return str_x==str_x[::-1]
-
时间复杂度: O ( n ) O(n) O(n)
-
空间复杂度: O ( n ) O(n) O(n)
题解二:反转一半数字
- 由于回文数具有对称性的特点,所以我们只需要反转后面一半的数字,与前面一半的数字判断是否一致即可
思路:
-
首先,考虑一些临界情况:所有负数都不可能是回文;除了 0 以外,所有个位是 0 的数字不可能是回文,因为最高位不等于 0。所以我们可以对所有小于0,大于 0 且个位是 0 的数字返回 false。
-
反转后半部分的数字
对于数字 1221,如果执行 1221 % 10,我们将得到最后一位数字 1,要得到倒数第二位数字,我们可以先通过除以 10 把最后一位数字从 1221 中移除,1221 / 10 = 122,再求出上一步结果除以 10 的余数,122 % 10 = 2,就可以得到倒数第二位数字。如果我们把最后一位数字乘以 10,再加上倒数第二位数字,1 * 10 + 2 = 12,就得到了我们想要的反转后的数字。如果继续这个过程,我们将得到更多位数的反转数字。
现在的问题是,我们如何知道反转数字的位数已经达到原始数字位数的一半?
由于整个过程我们不断将原始数字除以 10,然后给反转后的数字乘上 10,所以,当原始数字小于或等于反转后的数字时,就意味着我们已经处理了一半位数的数字了。
class Solution:
def isPalindrome(self, x: int) -> bool:
if x<0 or (x!=0 and x%10==0):
return False
elif x==0:
return True
else:
reverse_x=0
while x>reverse_x:
tmp=x%10
reverse_x=reverse_x*10+tmp
x//=10
return reverse_x==x or reverse_x //10==x
- 时间复杂度: O ( log n ) O(\log n) O(logn),对于每次迭代,我们会将输入除以 10,因此时间复杂度为 O(\log n)O(logn)。
- 空间复杂度: O ( 1 ) O(1) O(1)。我们只需要常数空间存放若干变量。