LeetCode-7-整数反转
题意描述:
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
注意: 假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
示例:
示例1:
输入: 123
输出: 321
示例2:
输入: -123
输出: -321
示例3:
输入: 120
输出: 21
解题思路:
Alice: 如果不加上[-2^31, 2^31-1]的限制就很好做了,出题人为什么要加这个限制呢 ?还说假设我们的环境只存的下32位的整数?
Bob:可能是想让我们只有 int
而不是 long
或者 long long
去做。
Alice: 但是用 long
会写的很简单啊,取余,整除,求和,之前在PAT上练过好几次了。
Bob: 嗯嗯,判题程序应该不能检测你是不是使用了 long
这个类型,应该也可以过的。
Alice: 不这么写,还能怎么过 ?
Bob: 用字符串啊,把字符串反转,然后判断两个字符串的大小顺序,如果一个正数的字符串表示的数值比 "-2147483648"小,一个负数的字符串表示的数值比“-2147483648”大,那就是合法的了。
Alice: ⊂(・(ェ)・)⊃ 这个简单,我也能写,最多5分钟搞定。
Bob: hhhhh
— 2000 Years later…
Alice: 我发现一种更好的写法,在反转数字拼接的最后一步预先判断是不是会超出范围就可以了,根本不用字符串!!你个大骗子!!!
Bob: o((⊙﹏⊙))oo((⊙﹏⊙))o
代码:
Python-方法 一:
class Solution:
def reverse(self, x: int) -> int:
temp = []
minus = False
if x < 0:
x *= -1
minus = True
elif x == 0:
return 0
while x != 0:
temp.append(x % 10)
x //= 10
ans = 0
for x in temp:
ans = ans * 10 + x
if minus:
ans *= -1
if ans > 2**31-1 or ans < -2**31:
return 0
else:
return ans
Python-方法二:
class Solution:
def reverse(self, x: int) -> int:
if x < 0:
prefix = -1
x *= -1
elif x == 0:
return 0
else:
prefix = 1
tmp = "".join([z for z in str(x)][::-1])
ret = int(tmp) * prefix
#print(ret)
if ret < -2**31 or ret > 2**31-1:
return 0
else:
return ret
c++方法一:
#include <math.h> // 幂运算的时候要用到里面的pow函数
class Solution {
public:
int reverse(int x) {
int prefix = 1;
long lx = long(x); // 注意-2^31直接变成正的,整型是存不下的,所以干脆先变成长整型好了。
if(lx < 0){
prefix = -1;
lx *= -1;
}else if(lx == 0){
return 0;
}
long ret = 0;
int tmp = 0;
while(lx > 0){ // 反转这个数字,按照个十百千...的顺序分开,再乘起来就是了。
tmp = lx % 10;
lx /= 10;
ret = ret * 10 + tmp;
}
ret *= prefix; // 别忘了,到底是个正数还是负数。
if(ret < int(-1 * pow(2, 31)) || ret > int(pow(2, 31))-1){ // 最后判断一下是否越界,注意pow()函数的返回值是float类型的,需要强制转换一下。
return 0;
}else{
return ret;
}
}
};
- Java 方法三:终极解决方案,计算过程中只用到了
int
类型而没有用到long
,完全当成处理字符串来做,虽然写的长,跑的慢,但是应该是符合题目要求的吧。如果题目把32位改成64位,128位,这样的方法,同样适用 !!鲁棒性!!
class Solution {
public int reverse(int x) {
if(x == 0){
return 0;
}
StringBuilder reversed = new StringBuilder("" + x).reverse(); //直接转换为字符串并且反转
int leftZeroNum = 0;
int prefix = 1;
for(int i=0; i<reversed.length(); ++i){
if(reversed.charAt(i) == '0'){
leftZeroNum += 1;
}else{
break;
}
}
reversed.delete(0, leftZeroNum); // 去掉左侧多余的零
if(reversed.charAt(reversed.length()-1) == '-'){
reversed.delete(reversed.length()-1, reversed.length());
prefix = -1;
} // 去掉右侧的负号
String tmp = "" + reversed;
//System.out.println(tmp + " " + prefix);
if(isValid(tmp, prefix)){ // 整型范围内的合法数字
//System.out.println("valid");
return Integer.parseInt(tmp) * prefix;
}else{
return 0;
}
}
public static boolean isValid(String x, int prefix){ // 判断一个数字是否在[-2^31, 2^31-1]的范围内
String positiveBound = "2147483647";
String minusBound = "2147483648";
if(x.length() < 10){
return true;
}else{
if(prefix == 1){
if(x.compareTo(positiveBound) <= 0){
return true;
}else{
return false;
}
}else if(prefix == -1){
if(x.compareTo(positiveBound) <= 0){
return true;
}else{
return false;
}
}else{
System.out.println("error prefix !!");
return false;
}
}
}
}
C++ 方法2,这才是最好的解法,代码量少,跑的又快,但是没那么好想出来。
class Solution {
public:
int reverse(int x) {
int res = 0;
int tmp = 0;
int max = int(pow(2, 31) - 1); // 获得最大值和最小值
int min = int(-1 * pow(2, 31));
while(x){
tmp = x % 10;
x /= 10;
if(res > max/10 || (res == max/10 && tmp > 7)){ // 反转一个正数数字到最后一步,发现即将生成的数字太大了,就返回零。
return 0;
}
if(res < min/10 || (res == min/10 && tmp > 8)){ // 反转一个负数数字到最后一步,发现即将生成的数字太小了,就返回零。
return 0;
}
res = res * 10 + tmp;
}
return res;
}
};
易错点:
- -2147483648 * -1 后整型已经存不下了。
- 0 这种边界值最好直接处理掉。
- 负数取余要特别注意。
总结:
- 把一个数拆开,再拼在一起,还有字符串的增删查改,比较排序都是常规基本操作,要熟悉到像呼吸一样自然 。