题目: 整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [,(
)− 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
示例:
输入:x = -123
输出:-321
输入:x = 123
输出:321
题目解读:
题目比较容易理解,数字反转和注意边界问题。
不能使用long类型(环境不允许存储 64 位整数)
这种纯数字问题,首先考虑数学方法,不要直接就转换为字符串再转整形。
方法一:官方方法
重复「弹出」x 的末尾数字,将其「推入」到rev的首位,直至 x 为 0。
可以不断取模乘以十,再相加。
基本思路
//x为需要反转的数字,pop为每次取模的数字(也就是每次x的最后一位数字)
int pop = x % 10;
x /= 10;
//rev存储反转后的数字
rev = rev * 10 + pop;
接下来只需要考虑边界问题
Integer.MIN_VALUE rev * 10 + pop
Integer.MAX_VALUE
为什么不能直接 Integer.MIN_VALUE rev
Integer.MAX_VALUE ?
如果 rev = rev * 10 + pop ,后再进行边界判断。在rev = rev * 10 + pop给rev赋值时,可能rev符合边界,但rev * 10 + pop超出边界范围,rev已经存储不下这么大的数字,再强给rev赋值则会出现超出边界被截去。所以要在给rev赋值前进行边界判断。
Integer.MIN_VALUE rev * 10 + pop
Integer.MAX_VALUE
1)情况一,x>0
分析 rev * 10 + pop Integer.MAX_VALUE
Integer.MAX_VALUE = −1=2147483647 = (MAX / 10) *10 +(MAX % 10)
= (MAX / 10) *10 + 7
即
rev * 10 + pop (MAX / 10) *10 + 7
移项易得:
(rev - (MAX / 10) )* 10 7 - pop
讨论不等式成立条件:
rev > (MAX / 10) 时, 左边必大于等于10,pop为 0 至 9,所有不等式必不成立。
rev = (MAX / 10) 时, 当 pop 7 ,不等式成立。
rev < (MAX / 10) 时, 左边必小于等于-10 ,pop为 0 至 9,所有不等式必成立。
当 rev = (MAX / 10) 时,若还能进入下次循环,则说明 x 的位数与 MAX / 10 位数相同,且这时rev推入的是最后一位数字,也就是x的最高位。由题 x 不超过 Integer.MAX_VALUE ,x最高位只能是1或者2。 pop 7的这个条件必满足,所以 rev = (MAX / 10) 时,不等式恒成立。
得,
当x > 0时,约束条件 rev (MAX / 10)。
2)情况2,x < 0
分析,全部转为正数处理最后加负号。rev * 10 + pop Integer.MIN_VALUE
Integer.MIN_VALUE = (MIN / 10) *10 + 8
即
pop - 8 (( MIN/10 ) - rev )* 10
讨论不等式成立条件:
rev > (MIN / 10) 时, 右边必小于等于 -10 ,pop为 0 至 9,所有不等式必不成立。
rev = (MIN / 10) 时, pop 8
rev < (MIN / 10) 时,右边必大于等于 10,pop为 0 至 9,所有不等式必成立。
和 x > 0 同理,最后推的数字只能是1或2,所以rev = (MIN / 10) 时, pop 8,恒成立。
得 rev (MIN / 10),加负号还原, (MIN / 10)
rev。
3)情况3,x = 0 时
rev = 0,写代码时作为特殊情况,直接返回0。
综上所得 (MIN / 10) rev
(MAX / 10)
public static int reverse(int x) {
//当 x = 0 时,不进入循环直接返回 0
int rev = 0;
while (x != 0) {
//每次推入后,判断。符合条件的证明 rev 无法再推入数字
if (rev < Integer.MIN_VALUE / 10 || rev > Integer.MAX_VALUE / 10){
return 0;
}
//pop 每次推入的数字
int pop = x % 10;
//rev 反转后的数字
rev = pop + rev * 10;
//此x 为 推出尾位数字的 x
x = x / 10;
}
return rev;
}
评论区看到一个方法用 if ((rev * 10) / 10 != rev) 判断是否可以继续推入数字。
public static int reverse(int x) {
//当 x = 0 时,不进入循环直接返回 0
int rev = 0;
while (x != 0) {
// 若 rev * 10 时已经溢出,rev * 10就会变化,而不是尾位加个 0。这时它再 /10 一定不在和原 rev相等。
//314748364 再 *10 已超出边界,314748364*10 打印是 -1147483656
if ((rev * 10) / 10 != rev) { //溢出
return 0;
}
//pop 每次推入的数字
int pop = x % 10;
//rev 反转后的数字
rev = pop + rev * 10;
//此x 为 推出尾位数字的 x
x = x / 10;
}
return rev;
}
这种方法也只限于Java,rev*10 超出边界后会变化,而不是乘以十。
例如:314748364 * 10 时超出边界,打印结果是 -1147483656
方法二:官方方法改良
减少最后只能推入1或2的推导,把rev = (MAX / 10) , pop 7 和 rev = (MIN / 10) , pop
8写到代码中。
public static int reverse(int x){
int rev = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
if (rev > Integer.MAX_VALUE/10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) return 0;
if (rev < Integer.MIN_VALUE/10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) return 0;
rev = rev * 10 + pop;
}
return rev;
}
基本和方法一原理一样。
方法三:转为字符串
字符串有个 reverse() 方法可以直接使用
原理简单,也是最容易想到的
public static int reverse(int x) {
try {
boolean flag = true;
if (x < 0) {
flag = false;
}
StringBuilder str = new StringBuilder(Integer.toString(Math.abs(x)));
str = str.reverse();
String s = str.toString();
//当转换超出边界时,就会报异常。
x = flag ? Integer.parseInt(s) : -Integer.parseInt(s);
} catch (NumberFormatException e) {
return 0;
}
return x;
}
总结
数字方面的问题,优先考虑用数学方法化简。而不要直接转为字符串操作。
本来过年时候第十题就写完了,本打算屯着,年后不出差稳定了,每周写一两篇。奈何有要继续出差最近刚忙完,不需要天天加班了。可以慢慢调整生活和学习了
博客探讨了如何在不使用64位整数的情况下,反转一个32位有符号整数,并处理反转过程中的边界问题。通过数学方法实现数字反转,避免字符串转换,同时分析了不同情况下的边界条件,包括正数、负数和零的情况。文章还提到了两种不同的Java实现方式,并指出在处理溢出时的微妙之处。最后,提到了使用字符串反转方法的简便性,但指出这种方法可能不适用于所有语言。
2623

被折叠的 条评论
为什么被折叠?



