9. 回文数

  突然发现  leetcode  上有每日一题的机制,吧唧嘴,然后我就很开心的捡了起来,然后一来就是两道困难的题目,喵喵喵,我个菜菜,到今天才写出一道简单的题目,来看看题,

回文数,很简单的一道题目,解法思路也很多,进阶的限制如果没有限制不给用 long 的话 可以考虑直接逆向构造,好了,先不说这么多,我们从初级的开始。

  按这道题的意思最开始应该用字符串,喵喵喵,姑且认为字符串是最简单的解法吧,其实最开始我还真的没想过用字符串解,因为逆向构造就很香啊。

如果 用C语言 我们需要知道 C 的一个函数 sprintf,具体的用法自行百度,这个函数的功能就是 将 数据 按某种数据类型转换到,举个例子:

sprintf(buf, "%d", x);

  这样子我们就把 x 中的值 以 %d 的 数据格式写入了 buf 中

所以这道题目的第一步就解决了,然后第二步,回文检查,检查回文就很简单了,1/2 比较就好了, 没什么难点

代码如下:

bool isPalindrome(int x){
    if (x < 0)
    {
        return false;
    }
    char buf[12] = {0};
    sprintf(buf, "%d", x);
    int len = strlen(buf);
    
    for (int i = 0; i < len/2; i++)
    {
        if (buf[i] != buf[len-i-1])
        {
            return false;
        }
    }
    
    return true;
}
View Code

现在我们来思考一下,新的方法构造法,如我们所想 如果数字是回文,那么逆向构造后等于原数,

这里有个难点,如何知道未知长度的整型数据的每一位, 并且加和在一起呢?

  我们来看两个数: 123, 4567  

  如果我们知道长度 那么 取各个位因该就是:

    123%10,123/10    4567%10  4567/10

    12%10,  12/10      456%10   456/10

    1%10,   1/10    45%10    45/10

                 5%10    5/10

  很容易就发现 我们一直重复的语句就是%10,和/10,所以 如果 数字是 a,则有

      while ()

      {

        sum[i++] = a%10;

        a /= 10;

      }

   那么问题来了,循环的关键问题,如何停止呢,重新来看看,上面的例子,嗯, 无论这个数值是什么,最后取完最后一位后这个值就是 0 了,所以只要a为0程序就停止了,那么代码就是这个样子了

      while (a)

      {

        sum[i++] = a%10;

        a /= 10;

      }

  接下来就是这个,逆向构造的核心,如何反向构造这个数, 如果a = 12345, 那么resa = 54321,很显然我们每次取余取出的都是最后一位

那么我们是不是只需要将 最后一位加到 sum 上后再乘以权重10 即可即 sum = a%10+sum*10 或者 是 sum += a%10;  sum*=10;

所以代码就变成了这样

      while (a)
      {
        sum = a%10+sum*10;
        a /= 10;
      }

  余下来的就是这个程序需要考虑的细枝末节了,例 如 负数,肯定就是不是回文数, 10 肯定不是 回文,1010,1000这些 右边为0 的数都不是 回文数

bool isPalindrome(int x)
{
    if (x < 0 || (0 == x % 10 && x != 0))
    {//过滤小数, 排除 右边为 0 的数,注意不是 0
        return false;
    }

    // 保留下 x 的原本的值
    int resx = x;
    int num = 0;
    while (x)
    {
        num = x % 10 + num * 10;
        x /= 10;
    }
    if (resx == num)
    {
        return true;
    }
    return false;
}

  但是 这样能AC吗,显然不能 int 最大值为 2^31 次方 21亿多,最后一位是7,一但逆向构造那就是70多亿所以肯定不行,这时候让我们想一想,传进来的肯定是整数范围,如果是回文数那么,无论如何都不会超过整数范围, 无论是正向构造还是逆向构造都不会溢出,也就是溢出的一定不是回文数,所以需要加一个预判

最后生成代码

bool isPalindrome(int x)
{
    if (x < 0 || (0 == x % 10 && x != 0))
    {//过滤小数, 排除 右边为 0 的数,注意不是 0
        return false;
    }

    // 保留下 x 的原本的值
    int resx = x;
    int num = 0;
    while (x)
    {
        if (num > INT_MAX / 10)
        {   //溢出判断
            return false;
        }
        num = x % 10 + num * 10;
        x /= 10;
    }
    if (resx == num)
    {
        return true;
    }
    return false;
}
View Code

  原本这个,我想用cpp的 try catch 结果发现 没有溢出异常,拉倒了

上面这种方法用了枝减法,时间复杂度为O(n),取决于数字长度,其实还有一个O(log(n))的算法

  这个是基于上面那个算法实现的,基于溢出的优化,全部逆向构造会溢出,那么咱们就逆向构造一半,如果左边等于右边那么这就是回文,

这里的最大问题是 如何到一半停下来,思考一下,假设 x 为 数据, y 为构造数据,那么 y 是不是在一直变大,x是不是一直在变小,那是不是当 x > y 的时候 while 就会执行

如果x <= y这个数是不是就是 就刚好到一半,所以 while (x > y) 就是while 的停止条件,到这里,循环就结束

        while (num < x)
        {
            num *= 10;
            num += (x % 10);
            x /= 10;
        }

不过我们还需要考虑一个细节,如果数据 是 1221 没有任何问题, 但是如果 12321 那么 y = 123 ,x = 21 明明是回文数 但是 x != y,凡是奇数长度的数据的都会出现这个问题所以,我们需要进行特殊处理

在判断的地方写为  if (x == num || x == y / 10) 这样子就可以去掉奇数的限制完美解决这个问题,

代码如下:

bool isPalindrome(int x)
{
    if (x < 0 || (x % 10 == 0 && x != 0))
    {
        // 特殊情况:
        // 如上所述,当 x < 0 时,x 不是回文数。
        // 同样地,如果数字的最后一位是 0,为了使该数字为回文,
        // 则其第一位数字也应该是 0
        // 只有 0 满足这一属性
        return false;
    }

    int resNum = 0;
    while (x > resNum)
    {
        resNum = resNum * 10 + x % 10;
        x /= 10;
    }

    // 当数字长度为奇数时,我们可以通过 resNum/10 去除处于中位的数字。
    // 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,resNum = 123,
    // 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除
    return x == resNum || x == resNum / 10;
}
View Code

  正如上面所说时间复杂度为log(n),只需要上面一半的时间.

目前只能想到这些方法,如有不当望指正.

    leetcode不易,诸君共勉!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值