力扣9.回文数-转字符双指针和反转数字

问题描述

给定一个整数 x,判断它是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

复制代码
输入:x = 121
输出:true

示例 2:

复制代码
输入:x = -121
输出:false
解释:从左向右读为 -121,从右向左读为 121-,因此不是回文数。

示例 3:

复制代码
输入:x = 10
输出:false
解释:从右向左读为 01,因此不是回文数。

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

解法一:字符串转换法(简单直观)

这是最直观的解法,将整数转换为字符串,然后用双指针比较。

java 复制代码
public boolean isPalindrome(int x) {
    // 将整数转换为字符串
    String str = String.valueOf(x);
    int left = 0, right = str.length() - 1;
    
    // 双指针向中间移动比较
    while (left < right) {
        if (str.charAt(left) != str.charAt(right)) {
            return false;
        }
        left++;
        right--;
    }
    
    return true;
}

复杂度分析:

  • 时间复杂度:O(n),其中 n 是数字的位数

  • 空间复杂度:O(n),需要额外的字符串存储空间

优缺点:

  • ✅ 代码简单,易于理解

  • ❌ 需要额外空间创建字符串

  • ❌ 不符合进阶要求

解法二:完全反转数字法

不转换为字符串,直接通过数学运算反转整个数字,然后与原数字比较。

java 复制代码
public boolean isPalindrome(int x) {
    // 负数不可能是回文数
    if (x < 0) {
        return false;
    }
    
    int reversed = 0;
    int original = x;  // 保存原始值
    
    // 反转数字
    while (x != 0) {
        // 每次取出x的个位数,加到reversed的末尾
        reversed = reversed * 10 + x % 10;
        x /= 10;  // 去掉x的个位数
    }
    
    // 比较反转后的数字与原始数字
    return reversed == original;
}

复杂度分析:

  • 时间复杂度:O(log₁₀x),循环次数等于数字的位数

  • 空间复杂度:O(1),只使用了常数级别的额外空间

注意事项:

这种方法有一个潜在问题:反转后的数字可能超出int的范围(虽然对于回文数判断来说,溢出不会影响结果正确性)。

解法三:反转一半数字法(最优解)

这是最高效的解法,只需要反转数字的一半,然后与另一半比较。

java 复制代码
public boolean isPalindrome(int x) {
    // 特殊情况:
    // 1. 负数不是回文数
    // 2. 以0结尾的数(0本身除外)不是回文数
    if (x < 0 || (x % 10 == 0 && x != 0)) {
        return false;
    }
    
    int reversedHalf = 0;
    // 当原始数字大于反转数字时,继续循环
    while (x > reversedHalf) {
        // 将x的个位数加到reversedHalf的末尾
        reversedHalf = reversedHalf * 10 + x % 10;
        x /= 10;  // 去掉x的个位数
    }
    
    // 对于偶数位数字:x == reversedHalf
    // 对于奇数位数字:x == reversedHalf / 10(去掉中间位)
    return x == reversedHalf || x == reversedHalf / 10;
}

复杂度分析:

  • 时间复杂度:O(log₁₀x/2),只需要反转一半的数字

  • 空间复杂度:O(1),只使用了常数级别的额外空间

为什么这是最优解?

  1. 避免了完全反转可能导致的溢出问题

  2. 减少了一半的循环次数

  3. 空间复杂度最低

  4. 代码简洁,逻辑清晰

性能对比

方法 时间复杂度 空间复杂度 优点 缺点
字符串转换 O(log₁₀x) O(log₁₀x) 简单直观 额外空间开销
完全反转 O(log₁₀x) O(1) 空间最优 可能溢出
反转一半 O(log₁₀x/2) O(1) 最优解 -

边界情况处理

在实现回文数判断时,需要考虑以下边界情况:

  1. 负数:所有负数都不是回文数

  2. 末尾为0:除了0本身,所有以0结尾的数字都不是回文数

  3. 整数溢出:在反转数字时要注意溢出问题

  4. 单个数字:0-9都是回文数

面试建议

在面试中遇到这个问题时,我建议:

  1. 先问清楚要求:是否需要考虑负数?是否允许转换为字符串?

  2. 从简单方法开始:先提出字符串转换法,展示基本思路

  3. 逐步优化:提出完全反转数字法,分析可能的问题(溢出)

  4. 提出最优解:展示反转一半数字法,解释为什么这是最优解

  5. 讨论边界情况:展示对边界情况的考虑

  6. 准备其他解法:如果时间允许,可以提一下数学方法

常见面试问题

  1. "为什么反转一半数字的方法更好?"

    • 答:因为它避免了完全反转可能导致的溢出问题,并且减少了一半的计算量。
  2. "如何处理负数?"

    • 答:根据回文数的定义,负数都不是回文数,因为负号在前端。
  3. "如果数字特别大,你的方法还能工作吗?"

    • 答:反转一半数字的方法不会反转整个数字,所以不会溢出。对于数学方法,我们只比较最高位和最低位,也不会溢出。

总结

判断整数是否是回文数有多种解法,每种解法都有其适用场景:

  • 日常编程:字符串转换法最简单直接

  • 面试场景:反转一半数字法最优,体现了对问题的深入理解

  • 算法竞赛:根据具体限制条件选择合适的方法

掌握多种解法不仅能帮助我们解决具体问题,更能提升我们分析问题和优化算法的能力。

相关推荐
张李浩21 小时前
Leetcode 054螺旋矩阵 采用方向数组解决
算法·leetcode·矩阵
big_rabbit050221 小时前
[算法][力扣101]对称二叉树
数据结构·算法·leetcode
WolfGang00732121 小时前
代码随想录算法训练营 Day11 | 二叉树 part01
数据结构
美好的事情能不能发生在我身上21 小时前
Hot100中的:贪心专题
java·数据结构·算法
myloveasuka21 小时前
Java与C++多态访问成员变量/方法 对比
java·开发语言·c++
2301_8217005321 小时前
C++编译期多态实现
开发语言·c++·算法
Andya_net1 天前
Spring | @EventListener事件机制深度解析
java·后端·spring
xixihaha13241 天前
C++与FPGA协同设计
开发语言·c++·算法
lang201509281 天前
18 Byte Buddy 进阶指南:解锁 `@Pipe` 注解,实现灵活的方法转发
java·byte buddy
重庆小透明1 天前
【java基础篇】详解BigDecimal
java·开发语言