Leetcode - 回文数问题解析

系列 - Leetcode 问题学习与解析

开始先吐槽一下:Leetcode 的代码编辑器真的是太难受了,没有智能提示,也不能 Debug, 需要每个月交 79!!这也太贵了!

注意
因为最近想要去欧美发展,而那边更注重算法,所以想开始学习算法并练习。所以,此系列文章可能都会比较啰嗦, 也可能会包含部分错误, 因为都在学习中,为了更好的生活,共勉!
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public boolean isPalindrome(int x) {
    if (x < 0 || (x % 10 == 0 && x != 0)) {
        return false;
    }

    int endNumber = 0;
    while (endNumber < x) {
        endNumber = x % 10 + endNumber * 10;
        x = x / 10;
    }
    if (endNumber == x || endNumber / 10 == x) {
        return true;
    }
    return false;
}
原始问题

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

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

示例 1:

输入:x = 121 输出:true

示例 2:

输入:x = -121 输出:false 解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入:x = 10 输出:false 解释:从右向左读, 为 01 。因此它不是一个回文数。

提示:

-231 <= x <= 231 - 1

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

如果 x 是回文数那就返回 true 否则返回 false。按照题目中给的示例,可以明显的看出如果 x 是负数必然不是回文数,所以我们可以快速的排除掉一半的情况。

如果使用暴力解法,我们需要考虑的情况就是将数字转成 char 数组,然后分析对比每个相对索引是否相同即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
   public boolean isPalindrome(int x) {
    if (x < 0) {
        return false;
    }
    String xString = Integer.toString(x);
    char[] xChars = xString.toCharArray();
    for (int i = 0; i < xChars.length; i++) {
        if (i == xChars.length / 2) {
            break;
        }
        if (xChars[i] != xChars[xChars.length - i - 1]) {
            return false;
        }
    }
    return true;
}

我们开始就判断是否为负数,排除一半的情况。

之后将数字转成 char 数组,方便循环处理。

然后判断第一个字符和最后一个字符是否相同,并依次增加。

这个解法比较简单,时间复杂度是 $ O(\log_{10}(x)) $ ,因为要查询所有字符,空间复杂度也是 $O(n)$,因为要存储所有字符。

为什么是 $ O(\log_{10}(x)) $
有些人可能疑惑:既然数字的每一个位数都要循环一遍,那不应该是 $O(n)$ 么?你说的对,但是现在 n 实际上是输入 x,也就是 121 之类的。它的位数是 3,实际上循环两次解决问题。而 $ O(\log_{10}(x)) $ 的值,也就是 2。

如果只使用数字的话,只要将数字反转就好了,例如 12345678 反转成 87654321 就好了,最先考虑到的就是除余。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  public boolean isPalindrome(int x) {
    if (x < 0 || x % 10 == 0) {
        return false;
    }

    int endNumber = 0;
    int finalX = x;
    while (x != 0) {
        endNumber = x % 10 + endNumber * 10;
        x = x / 10;
    }
    if (endNumber == finalX) {
        return true;
    }
    return false;
}

通过循环除余就可以拿到最终的结果,只需要判断是否与原数字是否相同就好。

至于数字大小超过数字范围,如果反转过程中超过范围那一定不是回文数,回文数正反相同,所以绝对不会超范围。

这种方式时间复杂度为 $ O(\log_{10}(x)) $ 因为要循环。空间复杂度是 $O(1)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
   public boolean isPalindrome(int x) {
    if (x < 0 || x % 10 == 0) {
        return false;
    }

    int endNumber = 0;
    while (endNumber < x) {
        endNumber = x % 10 + endNumber * 10;
        x = x / 10;
    }
    if (endNumber == x || endNumber / 10 == x) {
        return true;
    }
    return false;
}

刚刚的方法是循环了所有位,而判断回文数只需要对折一半就好了。也就是循环一半就好。

需要考虑的是,我们怎么知道是不是到达一半了?我们只需要判断反转的数字是否大于需要处理的数字就好。可以看个例子:

需要处理的值反转值等待处理的值
121112
12121

在做第三次处理的时候发现反转值是 12 剩余需要反转的值为 1 所以就知道已经到一半了

这样我们就可以得到反转值的一半了。而且可以发现如果数字的位数是奇数,中间会有一个多余值 2 在判断的时候我们只要移除多余值就好。

这样,我们的时间复杂度是 $ O(\log_{10}(x)) $ 空间复杂度是 $O(1)$,整体上是优于上个算法的,因为整个算法只需要处理一半的值。


需要注意的是 0 也是回文数,需要注意。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    public boolean isPalindrome(int x) {
        if (x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }

        int endNumber = 0;
        while (endNumber < x) {
            endNumber = x % 10 + endNumber * 10;
            x = x / 10;
        }
        if (endNumber == x || endNumber / 10 == x) {
            return true;
        }
        return false;
    }

相关内容