zoukankan      html  css  js  c++  java
  • LeetCode(9):回文数

    Easy!

    题目描述:

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

    示例 1:

    输入: 121
    输出: true
    

    示例 2:

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

    示例 3:

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

    进阶:

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

    判断一个整数是否是回文数。不能使用辅助空间。

    一些提示:

    负整数可以是回文数吗?(例如 -1)

    如果你打算把整数转为字符串,请注意不允许使用辅助空间的限制。

    你也可以考虑将数字颠倒。但是如果你已经解决了 “颠倒整数” 问题的话,就会注意到颠倒整数时可能会发生溢出。你怎么来解决这个问题呢?

    本题有一种比较通用的解决方式。

    解题思路:

    意味着这道验证回文数字的题如果将数字转为字符串,就变成了验证回文字符串的题,没啥难度了,我们就直接来做follow up吧,这道验证回文数字的题不能使用额外空间,不能转为字符串,然后来验证回文字符串。而是直接对整数进行操作,我们可以利用取整和取余来获得我们想要的数字,比如 1221 这个数字,如果 计算 1221 / 1000, 则可得首位1, 如果 1221 % 10, 则可得到末尾1,进行比较,然后把中间的22取出继续比较。代码如下:

    C++参考答案一:

     1 class Solution {
     2 public:
     3     bool isPalindrome(int x) {
     4         if (x < 0) return false;
     5         int div = 1;
     6         while (x / div >= 10) div *= 10;
     7         while (x > 0) {
     8             int left = x / div;
     9             int right = x % 10;
    10             if (left != right) return false;
    11             x = (x % div) / 10;
    12             div /= 100;
    13         }
    14         return true;
    15     }
    16 };

    我们再来看一种很巧妙的解法,还是首先判断x是否为负数,这里我们可以用一个小trick,因为我们知道整数的最高位不能是0,所以回文数的最低位也不能为0,数字0除外,所以如果发现某个正数的末尾是0了,也直接返回false即可。好,下面来看具体解法,要验证回文数,那么就需要看前后半段是否对称,如果把后半段翻转一下,就看和前半段是否相等就行了。所以我们的做法就是取出后半段数字,进行翻转,具体做法是,每次通过对10取余,取出最低位的数字,然后加到取出数的末尾,就是将revertNum乘以10,再加上这个余数,这样我们的翻转也就同时完成了,每取一个最低位数字,x都要自除以10。这样当revertNum大于等于x的时候循环停止。由于回文数的位数可奇可偶,如果是偶数的话,那么revertNum就应该和x相等了;如果是奇数的话,那么最中间的数字就在revertNum的最高位上了,我们除以10以后应该和x是相等的,参见代码如下:

    C++参考答案二:

     1 class Solution {
     2 public:
     3     bool isPalindrome(int x) {
     4         if (x < 0 || (x % 10 == 0 && x != 0)) return false;
     5         int revertNum = 0;
     6         while (x > revertNum) {
     7             revertNum = revertNum * 10 + x % 10;
     8             x /= 10;
     9         }
    10         return x == revertNum || x == revertNum / 10;
    11     }
    12 };

    下面这种解法由网友zeeng提供,如果是palindrome,反转后仍是原数字,就不可能溢出,只要溢出一定不是palindrome返回false就行。

    C++参考答案三:

     1 class Solution {
     2 public:
     3     bool isPalindrome(int x) {
     4         if (x < 0 || (x % 10 == 0 && x != 0)) return false;
     5         return reverse(x) == x;
     6     }
     7     int reverse(int x) {
     8         int res = 0;
     9         while (x != 0) {
    10             if (res > INT_MAX / 10) return -1;
    11             res = res * 10 + x % 10;
    12             x /= 10;
    13         }
    14         return res;
    15     }
    16 };

    官方解答:

    方法:反转一半数字

    直觉

    映入脑海的第一个想法是将数字转换为字符串,并检查字符串是否为回文。但是,这需要额外的非常量空间来创建问题描述中所不允许的字符串。

    第二个想法是将数字本身反转,然后将反转后的数字与原始数字进行比较,如果它们是相同的,那么这个数字就是回文。 但是,如果反转后的数字大于int.MAX,我们将遇到整数溢出问题。

    按照第二个想法,为了避免数字反转可能导致的溢出问题,为什么不考虑只反转int 数字的一半?毕竟,如果该数字是回文,其后半部分反转后应该与原始数字的前半部分相同。

    例如,输入1221,我们可以将数字“1221”的后半部分从“21”反转为“12”, 并将其与前半部分“12”进行比较,因为二者相同,我们得知数字1221是回文。

    让我们看看如何将这个想法转化为一个算法。

    算法

    首先,我们应该处理一些临界情况。所有负数都不可能是回文,例如:-123不是回文,因为-不等于3。所以我们可以对所有负数返回false。

    现在,让我们来考虑如何反转后半部分的数字。 对于数字1221,如果执行1221 % 10,我们将得到最后一位数字1,要得到倒数第二位数字,我们可以先通过除以10把最后一位数字从1221中移除,1221 / 10 = 122,再求出上一步结果除以10的余数,122 % 10 = 2,就可以得到倒数第二位数字。如果我们把最后一位数字乘以10,再加上倒数第二位数字,1 * 10 + 2 = 12,就得到了我们想要的反转后的数字。 如果继续这个过程,我们将得到更多位数的反转数字。

    现在的问题是,我们如何知道反转数字的位数已经达到原始数字位数的一半?

    我们将原始数字除以10,然后给反转后的数字乘上10,所以,当原始数字小于反转后的数字时,就意味着我们已经处理了一半位数的数字。

    C#代码:

    public class Solution {
        public bool IsPalindrome(int x) {
            // Special cases:
            // As discussed above, when x < 0, x is not a palindrome.
            // Also if the last digit of the number is 0, in order to be a palindrome, 
            // the first digit of the number also needs to be 0.
            // Only 0 satisfy this property.
            if(x < 0 || (x % 10 == 0 && x != 0)) {
                return false;
            }
    
            int revertedNumber = 0;
            while(x > revertedNumber) {
                revertedNumber = revertedNumber * 10 + x % 10;
                x /= 10;
            }
            
            // When the length is an odd number, we can get rid of the middle digit by revertedNumber/10
            // For example when the input is 12321, at the end of the while loop we get x = 12, revertedNumber = 123, 
            // since the middle digit doesn't matter in palidrome(it will always equal to itself), we can simply get rid of it.
            return x == revertedNumber || x == revertedNumber/10;
        }
    }

    复杂度分析

    • 时间复杂度:O(log10​​n), 对于每次迭代,我们会将输入除以10,因此时间复杂度为 O(log10​​n)。
    • 空间复杂度:O(1)。 
  • 相关阅读:
    台州 OJ 3847 Mowing the Lawn 线性DP 单调队列
    洛谷 OJ P1417 烹调方案 01背包
    快速幂取模
    台州 OJ 2649 More is better 并查集
    UVa 1640
    UVa 11971
    UVa 10900
    UVa 11346
    UVa 10288
    UVa 1639
  • 原文地址:https://www.cnblogs.com/ariel-dreamland/p/8715286.html
Copyright © 2011-2022 走看看