9.回文数
9. 回文数
鲁迅说过,我不知道什么是算法,但是我知道二哥写的 LeetCode 题解真不错。
题意
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
• 例如,121 是回文,而 123 不是。
示例
输入:x = 121
输出:true
输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。
分析
读完这道题目,我们需要先搞清楚什么是回文,回文就是正着读和反着读都是一样的,比
如 121,12321,1234321 等等,那么我们如何判断一个数是不是回文数呢?
我第一时间想到的是将数字转成字符串,然后通过 StringBuilder 的 reverse() 方法来翻转
字符串,最后比较两个字符串是否相等,如果相等的话,就说明是回文数,否则就不是回
文数。
class Solution {
public boolean isPalindrome(int x) {
// 数字转字符串
String str = String.valueOf(x);
// 把字符串放入 StringBuilder 中
StringBuilder sb = new StringBuilder(str);
// 利用 StringBuilder 的 reverse() 方法翻转字符串
// 然后调用 equals() 方法比较两个字符串是否相等
return sb.reverse().toString().equals(str);
}
}
咦,竟然能打败 30% 的 Java 用户,别骗我啊,这么简单的题,这么低的效率,我不信。
009.%E5%9B%9E%E6%96%87%E6%95%B0-20240103194017.png
当然了,看似简单的代码背后,其实蕴藏了很多玄机,我们来分析一下。
①、String.valueOf(x)
这个方法可以把整数转成字符串,我在《二哥的 Java 进阶之路》上曾讲过这个方法。
当然了,也可以通过 x + "" 来完成转换,不过 + 号操作符的背后,其实调用的是
StringBuilder 的 append() 方法。
这个我们在讲字符串拼接时曾讲过,不知道大家还记得不?
那后面我们其实又 new 了一个 StringBuilder 对象,所以这里就直接采用
String.valueOf(x) 来完成转换了。
②、StringBuilder sb = new StringBuilder(str);
这里我们把字符串放入 StringBuilder 中,这个也是我在《二哥的 Java 进阶之路》上曾讲
过的,不知道大家还记得不?
包括 reverse() 方法,前面的题解 007.整数反转 也讲过了,这里就不再赘述了。
③、equals(str)
比较两个字符串是否相等,这个我在《二哥的 Java 进阶之路》上也曾讲过了,源码也带
着大家分析过。
分析 2
前面我们反转的是整个数字,那其实我们可以只反转一半的数字,比如 1234321,我们
只需要反转后面一半的数字 321,然后和前面一半的数字 123 进行比较,如果相等,就说
明是回文数,否则就不是回文数。
奇数的话去掉中间的数字。
来看题解,主要用到的还是乘法、取余和除法:
class Solution {
public boolean isPalindrome(int x) {
// 负数不是回文数
// 如果数字的最后一位是 0,为了使该数字为回文,
// 则其第一位数字也应该是 0,只有 0 满足这一属性
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int revertedNumber = 0;
while (x > revertedNumber) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}
// 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
// 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,
revertedNumber = 123,
// 由于中间的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
return x == revertedNumber || x == revertedNumber / 10;
}
}
1. 处理特殊情况:
– 负数不是回文数,因为有负号。
– 如果一个数的最后一位是 0,要使其为回文,则第一位数字也必须是 0。唯一
满足这个条件的数字就是 0。
2. 反转一半的数字:
– 我们通过 while 循环将最后一位数字移动到 reversedNumber 的末尾,同时将
原始数字除以 10,来反转数字的后半部分。
– 当原始数字小于或等于反转后的数字时,意味着我们已经处理了一半或超过一
半的数字。
3. 比较前半部分和后半部分:
– 如果数字的长度是偶数,反转后的数字应与剩余的原始数字相等。
– 如果数字长度是奇数,则通过将反转后的数字除以 10 去除中间的数字,然后
比较剩余部分。
我们来模拟一下整个题解的过程,比如 1234321:
当输入的数字是 1234321 时,我们来模拟一下判断回文数的过程。按照之前提供的算
法,我们将逐步反转数字的后半部分,并与前半部分进行比较。
初始状态
• 原始数字 x = 1234321
• 反转后的数字 revertedNumber = 0
迭代过程
1. 第一次迭代:
– 取 x 的最后一位:1234321 % 10 = 1
– 将这一位加入 revertedNumber:revertedNumber = 0 * 10 + 1 = 1
– 去掉 x 的最后一位:x = 1234321 / 10 = 123432
此时,x = 123432,revertedNumber = 1。
2. 第二次迭代:
– 取 x 的最后一位:123432 % 10 = 2
– 更新 revertedNumber:revertedNumber = 1 * 10 + 2 = 12
– 更新 x:x = 123432 / 10 = 12343
此时,x = 12343,revertedNumber = 12。
3. 第三次迭代:
– 取 x 的最后一位:12343 % 10 = 3
– 更新 revertedNumber:revertedNumber = 12 * 10 + 3 = 123
– 更新 x:x = 12343 / 10 = 1234
此时,x = 1234,revertedNumber = 123。
4. 第四次迭代:
– 取 x 的最后一位:1234 % 10 = 4
– 更新 revertedNumber:revertedNumber = 123 * 10 + 4 = 1234
– 更新 x:x = 1234 / 10 = 123
此时,x = 123,revertedNumber = 1234。
结束条件
在第四次迭代后,x(123)不再大于 revertedNumber(1234)。此时我们停止迭代。
比较前后半部分
• 现在,我们比较 x 和 revertedNumber / 10(1234 / 10 = 123)。
• 由于 x == revertedNumber / 10(123 == 123),我们可以判断输入的数字是回文
数。
很好理解,我们来看一下题解效率:
009.%E5%9B%9E%E6%96%87%E6%95%B0-20240103210929.png
咦,竟然只打败了 55% 的 Java 用户,看来还有优化的空间啊!
不应该啊,代码没有优化的空间了呀,把代码注释全部删掉,然后再执行一遍。
009.%E5%9B%9E%E6%96%87%E6%95%B0-20240103211725.png
好家伙,这次直接打败 98% 的 Java 用户了,看来注释也是有代价的,哈哈哈。
LeetCode 这波计算方式,我只能说服了,不过为了让大家更好的理解代码,所以我还是
会保留注释的。
总结
这道题目,我觉得最重要的就是理解题意,然后就是理解回文数的含义。
不管是用字符串来解决,还是用数学方法来解决,我觉得都是可以的。
Java 的优势不就是 JDK 帮我们提供了无数牛逼的 API 吗?如果不用的话,学 Java 干嘛?
直接学 C 语言不就行了?
简单梳理一下我们用到的基础知识:
• String.valueOf(x):把整数转成字符串,我在介绍字符串源码的时候讲过。
• reverse():字符串反转,我在介绍 StringBuilder 的时候讲过。
• equals():比较两个字符串是否相等,我在介绍 equals()与==的时候讲过。
当然了,要想学好算法,数学还是要好一点,这样才能更好地运用乘法、取余和除法来解
决问题。
力扣链接:https://leetcode.cn/problems/palindrome-number/