整数反转和回文数

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/HermitSun/article/details/102534747

在开始之前,我觉得应该默念一遍奥卡姆剃刀原理:“如无必要,勿增实体”。我感觉这一条原则在做算法的时候特别重要:是什么就是什么,不应该增加额外的元素。当然,在需要空间换时间(或者空间换时间)的时候,增加辅助元素是有必要的,并不冲突。

为什么这么说呢,且看这两道题目:

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31 − 1]。请根据这个假设,如果反转后整数溢出,那么就返回 0。

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

其实这两道题从本质上是一样的,都是对数字的反转。

但是按照常规思路,第一反应是什么?我觉得很多人(比如我)都会想到,先把数字变成字符串,然后进行反转。这个思路看起来很合理,但仔细一想就会觉得不对,因为明明是数字之间的操作,为什么要变成字符串呢?而且在反转整数的时候,还要对负号进行额外的处理。

/**
 * ------result------
 * memory: 8 MB (90%)
 * speed: 0 ms  (100%)
*/
int reverse(int x) {
    int res = 0;
    while (x != 0) {
        // INT32_MIN / -1会溢出,所以需要转换成long
        // 至于为什么不用res跟INT32_MAX比较,因为不能确定res的符号,所以范围可能会错误
        if (res != 0 &&
            (long)INT32_MAX / res < 10 &&
            (long)INT32_MIN / res < 10) {
            return 0;
        }
        res = res * 10 + x % 10;
        x /= 10;
    }
    return res;
}

其实这个做法有点取巧的意思,因为如果环境不支持long,这个做法就会失效;最好的做法是对最后一位进行判断。另外,我觉得涉及到倒序对数字的每一位进行操作的问题,秦九韶算法(也就是所谓的霍纳算法)都是值得考虑的,因为这个算法可以以O(n)的时间复杂度对每一位进行操作。

至于回文数,明明判断一下反转后的整数和反转之前是否相等就够了,为什么要引入字符串呢?但是因为回文数的特殊性,这题有特殊的解法:只反转一半,也就是修改一下反转结束的判断条件:

bool isPalindrome(int x) {
    // 负数不是回文数
    // 除了0,不存在最后一位为0的回文数
    if (x < 0 ||
        (x % 10 == 0 && x != 0)) {
        return false;
    }
    int res = 0;
    // 如果x <= res,说明已经过了一半了
    while (x > res) {
        res = res * 10 + x % 10;
        x /= 10;
    }
    // 如果是奇数,到这里还需要额外除以10;因为过去了一位
    return x == res || x == res / 10;
}

我感觉有时候是不是自己提高了算法题的难度……

猜你喜欢

转载自blog.csdn.net/HermitSun/article/details/102534747