算法小白刷力扣 3 - 回文数

题目描述

原题链接:https://leetcode.cn/problems/palindrome-number/description/


给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

  • 例如,121 是回文,而 123 不是。

示例 1:

输入:x = 121

输出:true

示例 2:

输入:x = -121

输出:false

解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入:x = 10

输出:false

解释:从右向左读, 为 01 。因此它不是一个回文数。

  • 提示:
    -2^31 <= x <= 2^31 - 1

进阶:你能不将整数转为字符串来解决这个问题吗?

解法一:暴力破解

根据回文数的特征,对任意大于两位的数字,对称位置上的两个数字相等即可,按照这种思路很容易想到循环比较对称位置上的数字的方式去实现,但由于无法直接对比对称位数字,就需要将数字转为字符串或字符数组(二者差别不大)进行比较判断,这种方式会额外增加空间,且数字转字符串也会增加耗时,是暴力破解的方式。

java 复制代码
class Solution {
    public boolean isPalindrome(int x) {
        if(x < 0) {
            return false;
        }
        if(x < 10) {
            return true;
        }
        // 将数字转为字符串
        String s = x + "";
        // 再转为字符数组
        char[] chars = s.toCharArray();
        // 因为只需要比较数字两边对称的部分,因此只需遍历 chars.length / 2 次,对数字长度为偶数和奇数都适用
        // 第[i]位和第 [chars.length-1-i]的数字为一对,如果每一对数字都相同则为回文数,否则只要有一对不相等,则不是回文数
        // 该方法利用了数组可以随机查询任意位置的数字的特性暴力循环判断
        for(int i = 0; i < chars.length / 2; i++) {
            if(chars[i] != chars[chars.length - 1 - i]) {
                return false;
            }
        }
        return true;
    }
}

思路二:全翻转比较

另一种思路是既然数字从前往后读和从后往前读是一样的,那只要将数字全部翻转过来,与原来的数字进行比较,如果两数相等则肯定就是回文数了。这种方式理解起来最简单直接,如果知道数字怎么翻转,实现起来也很简单。需要注意的是,因为要比较翻转前后的数字,所以不能直接修改原数x,在翻转前需要替换原数字为新数进行翻转,然后将翻转结果与原数字比较。

java 复制代码
class Solution {
    public boolean isPalindrome(int x) {
        if(x < 0) {
            return false;
        }
        if(x < 10) {
            return true;
        }
        // 将数字完全翻转
        int r = 0;
        int temp = x;
        while (temp != 0) {
            r = r * 10 + temp % 10;
            temp /= 10;
        }
        // 翻转后的数字如果与原数字相等,则肯定为回文数
        return r == x;
    }
}

用这种方式提交后可以通过 LeetCode 的测试用例,但是这种情况存在翻转后的数字超出最大数范围的可能。无论从时间复杂度(O(n))还是准确性上来说都不是最优解。

要理解这种方式,就需要知道如何将一个整数进行翻转。思路可以用下面一张图解释:

整体的代码如下:

java 复制代码
public int reverseNumber(int num) {
    int i = 0;
    while (num != 0) {
        i = i * 10 + num % 10;
        // 移位
        num = num / 10;
    }
    return i;
}

思路三:半翻转比较

这种算法是最优的,但可惜我没想到,是在看了 LeetCode 官方题解后才知道的,但在思考的过程中也曾擦肩而过 😃

既然是比较对称位上的数字,那何不只取一半的数字进行比较呢,这样耗时更少且不会出现溢出的可能。例如,对于数字 1221这种偶数位数字,只需翻转取出后两位为12,其和前两位数12相等,即为回文数;又对于12321奇数位数字,翻转取出后两位12,不考虑中间位数字3,其和前两位12也相等,是回文数。

但是数字不能轻易像字符串那样知道总共有多少位,如何知道翻转到一半呢?这个问题是这种解法的核心所在。

LeetCode官方题解的思路是:"由于整个过程我们不断将原始数字除以 10,然后给反转后的数字乘上 10,所以,当原始数字小于或等于反转后的数字时,就意味着我们已经处理了一半位数的数字了。" 这个想法很新奇(个人脑子认为),它认为已经翻转的数字位数大于或等于未翻转的数字位数,则肯定已经翻转了一半或超过一半,就停止翻转。此时,如果数字长度为偶数位,则只要翻转后的数字与还未翻转的数字相等即是回文数;如果数字长度为奇数,只要再去掉翻转后数字的最后一位,也就是原本数字的中间一位,其如果与还未翻转的数字相等,则是回文数。如上图(图片摘取自LeetCode官方题解),这种方法无需将数字转为字符串额外增加空间,而且由于是翻转一半,效率更高,肯定也不会溢出。代码如下:

java 复制代码
class Solution {
    public boolean isPalindrome(int x) {
        if(x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }
        int r = 0;
        // 每次翻转之前,只要未翻转的数字大于已翻转的数字,则说明还要继续翻转
        while (x > r) {
            r = r * 10 + x % 10;
            x /= 10;
        }
        // 翻转一半后两边数字比较有两种情况时为回文数:
        // 数字长度为偶数时 r == x, 例如对于 11, 1221
        // 数字长度为奇数时 r == x / 10, 例如对于 121, 12321
        return r == x || r / 10 == x;
    }
}
相关推荐
Youkiup29 分钟前
【重构数组,形成排列的最大长度】
算法
星夜孤帆39 分钟前
LeetCode之图的广度优先搜索
算法·宽度优先
忍界英雄40 分钟前
LeetCode: 673.最长子序列的数量 动态规划 时间复杂度O(n*n)
算法·leetcode·动态规划
Renascence.4091 小时前
力扣--649.Dota2参议院
java·数据结构·算法·leetcode
山脚ice1 小时前
【Hot100】LeetCode—62. 不同路径
算法·leetcode·职场和发展
小小工匠1 小时前
加密与安全_ sm-crypto 国密算法sm2、sm3和sm4的Java库
java·算法·安全·sm2·sm3·sm4
陈小唬1 小时前
树形结构构建的两种方式
java·数据库·算法
7yewh3 小时前
STM32常用数据采集滤波算法
stm32·嵌入式硬件·算法
不会编程的小江江3 小时前
【动态规划】(一)动态规划理论及基础题目
c++·算法·动态规划
qq_535246143 小时前
代码随想录 第九章 动态规划part03 01背包问题 一维 416. 分割等和子集
算法·动态规划·代理模式