题目描述🌍
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
示例 1:
输入:x = 123
输出:321
示例 2:
输入:x = -123
输出:-321
示例 3:
输入:x = 120
输出:21
示例 4:
输入:x = 0
输出:0
提示:
异常处理机制🚀
解题思路
提到反转,我们首先会想到库函数 reverse() 方法将字符串反转,但题目是对整数进行反转,所以我们需要将整数转换为字符串类型再操作。
🔮其次需要解决的是反转后的可能出现的整数溢出问题,当赋值给 int 类型的值溢出时,Java 和 C++ 会分别抛出 NumberFormatException/out_of_range 异常,既然这是个异常,那么就顺着题目 ”当反转后溢出返回 0“ 的要求,在捕获异常后不进行多余的处理,直接返回 0 即可,这不失为一个好方法。
Tip:有些数字可能是合法范围内的数字,但是反转过来就超过范围了。假设有
2147483643这个数字,它是小于最大的 32 位整数2147483647的,但是将这个数字反转过来后就变成了3463847412,这就比最大的 32 位整数还要大了,这样的数字是没法存到int里面的,所以肯定要返回 0(因为溢出)。
代码
Java
class Solution {
public int reverse(int x) {
// 判断x的正负性
boolean flag = x < 0 ? true : false;
StringBuilder reverse = new StringBuilder(Integer.toString(Math.abs(x))).reverse();
try {
Integer value = Integer.valueOf(reverse.toString());
// 根据x的正负性返回相应的反转后的整数
return flag ? -value : value;
} catch (NumberFormatException e) {
// Do nothing
}
return 0;
}
}
C++
class Solution {
public:
int reverse(int x) {
bool isNegative = x < 0 ? true : false;
// to_string(): int => string
string strX = to_string(x);
reverse(strX.begin(), strX.end());
try {
// stoi(): string => int
int value = stoi(strX);
// 若正确转换(无异常), 则可以返回
return isNegative ? -value : value;
} catch (const out_of_range &e) {
// cout << e.what() << endl;
}
// NULL在C++中就是0
// return NULL;
return 0;
}
};
皆因
reverse()反转函数
时间复杂度:
空间复杂度:
数学方法🚀
解题思路
首先我们想一下,怎么去反转一个整数?用栈?或者把整数变成字符串,再去反转这个字符串? 这两种方式是可以,但并不好。实际上我们只要能拿到这个整数的 “末尾数字” 就可以了。 以 12345 为例,先拿到 5,再拿到 4,之后是 3,2,1,我们按这样的顺序就可以反向拼接出一个数字了,也就能达到 “反转” 的效果。 怎么拿末尾数字呢?好办,用取模运算就可以了。
这么看起来,好像一个循环就搞定了,但是我们这里只考虑了正数,再考虑到负数,我们循环的终止条件就不是 x>0 ,而是 x!=0 了。 这样一来,无论正负,最后一次除法运算都会让其变成 0,且对于 12300 这样的数字,也可以完美地解决掉!
看起来这道题就这么解决了,但请注意,题目上还有这么一句:
反转后整数超过 32 位的有符号整数的范围 ,就返回 0。
隐含:有些数字可能是合法范围内的数字,但是反转过来就超过范围了。例如上文提到的数字 2147483643。这样的数字反转后没法存到 int 里面的,所以肯定需要提前判断,而不是等到最后一次赋值时出现溢出再处理(此时已报异常,来不及处理);所以,我们到【最大数的1/10】即末尾的前一位时,就要开始判断了:
如果某个数字大于 214748364 ,那后面就不用再判断了,肯定溢出了。
如果某个数字小于 -214748364 ,那后面就不用再判断了,肯定溢出了。
但如果某个数字等于 214748364 或 -214748364 ,肯定是不会溢出的,无需再判断:
- 以正数为例:其最后一位数必须大于
7才会造成溢出,但这是在反转后判断的,在反转数字前,该数字必是以>7的开头且位数等于Integer.MAX_VALUE,所以传形参时必然溢出,而实参是不会给出大于int类型的数(否则还没反转就异常了); - 负数同理。
代码
Java
class Solution {
public int reverse(int x) {
int remainder;
int res = 0;
while (x != 0) {
if ((res > Integer.MAX_VALUE / 10) || (res < Integer.MIN_VALUE / 10)) {
return 0;
}
remainder = x % 10;
res = res * 10 + remainder;
x /= 10;
}
return res;
}
}
C++
class Solution {
public:
int reverse(int x) {
int ret = 0;
int remainder;
while (x != 0) {
if ((ret > INT32_MAX / 10) || (ret < INT32_MIN / 10)) {
return 0;
}
remainder = x % 10;
ret = ret * 10 + remainder;
x /= 10;
}
return ret;
}
};
时间复杂度:,n 为 x 的位数
空间复杂度:
类型转换🚀
解题思路
优雅永不过时!
该题的难点就是解决溢出问题,我们换种思路:为什么一开始就用 int 型变量接收呢?为什么不直接用 long 型变量接收该数,等到返回时进行强制转换为 int 型再处理?这样一来,我们就不用在 while 语句中不断进行 if 条件的判断了。
long型向int型强制转换可能出现数据溢出的现象,从而导致前后数据不一;据此原理来设计return语句,顺势完成题目反转整数溢出返 0 的要求。
代码
Java
class Solution {
public int reverse(int x) {
long n = 0;
while (x != 0) {
n = n * 10 + x % 10;
x = x / 10;
}
return (int) n == n ? (int) n : 0;
}
}
C++
class Solution {
public:
int reverse(int x) {
long ret = 0;
while (x != 0) {
ret = ret * 10 + x % 10;
x /= 10;
}
return (int) ret == ret ? int(ret) : 0;
}
};
时间复杂度:,n 为 x 的位数
空间复杂度:
知识点🌓
Java
1. char[]、String、Integer 互转
| Class | Modifier and Type | Method and Description |
|---|---|---|
| Integer | static String | toString(int i):Integer —— String |
| Integer | static Integer | valueOf(String s):String —— Integer |
| String | char[] | toCharArray():String —— char[] |
| String | static String | valueOf(char[] data):char[] —— String |
2. 字符串反转的四种方法:
参考博客:blog.csdn.net/qq_43701760…
C++
1. to_string() 库函数能将各种类型转换为 string 类型
浮点型可能无法正确转换
2. string 转 int:stoi() 库函数
还有其他类似:
stol()、stoll()
3. 关于 C++ 的 NULL 与 nullptr
⭐结论:
C 中的 NULL 是空指针 void *,而 C++ 中的 NULL 是 0.
所以 C++ 引入了 nullptr 这一新关键字来定义空指针,NULL 只能沦为 0.
编译器提供的头文件所做的处理:
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
C 的编译器是 gcc
C++ 的编译器是 g++
❤该问题的详细解答见:C++ 中 NULL 和 nullptr 的区别
4. 反向迭代器 (rbegin, rend) 与 (begin, end) 之间的区别
begin()指向容器的第一个元素end()指向容器的最后一个元素的下一个位置rbegin()指向容器的最后一个元素rend()指向容器的第一个元素的前一个位置
结合图片食用,理解效果更佳
⭐简单示例:
std::vector<int> vec = {1, 2, 3, 4, 5, 6};
// 正向迭代器
std::vector<int>::iterator it;
// 结果: 1 2 3 4 5 6
for (it = vec.begin(); it != vec.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// 逆向迭代器
std::vector<int>::reverse_iterator r_it;
// 结果: 6 5 4 3 2 1
for (r_it = vec.rbegin(); r_it != vec.rend(); ++r_it) {
cout << *r_it << " ";
}
最后🌅
该篇文章为 「LeetCode」 系列的 No.2 篇,在这个系列文章中:
- 尽量给出多种解题思路
- 提供题解的多语言代码实现
- 记录该题涉及的知识点
👨💻争取做到 LeetCode 每日 1 题,所有的题解/代码均收录于 「LeetCode」 仓库,欢迎随时加入我们的刷题小分队!