LeetCode刷题总结(三)7-9

本文主要总结了LeetCode的7-9题,包括整数反转、字符串转换整数(atoi)和回文数。讨论了如何处理整数反转时的溢出问题,字符串转整数时的空格、正负号处理,以及回文数的判断。文章提供了C++代码实例,并强调了在不同存储环境下如何避免溢出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(1)LeetCode7:整数反转

解决本题要有几个常识:

①. 对于一个整数来说,要把它的每一位数抠出来,就是先模10取出个位,再除以10,将个位去掉,循环该过程,知道全部都被去掉了。
while(x) { // x为被处理的数,当x为0时,即跳出循环
cout << x % 10;
x /= 10;
}
②. 剥离开来之后要还原,则为:
r = 0; // r 是目标值
r = r * 10 + 第一位;
r = r * 10 + 第二位
r = r * 10 + 第三位
… 直到所有数都用完了
当现在要逆序输出时,我们可以将两者合并:
r = 0;
while(x) {
r = r * 10 + x % 10;
x /= 10;
}
③. 还有一个情况,当 x 为负数时,当如何处理呢?
C ++ 有一个特殊的特性,负数模(%)10仍然是负数(注意这仅仅是C++中适用,然而从数学上说,一个数a对10取模,结果b应该满足:a - b = 10k , k = 自然数):

cout << 1234 % 10; // 4
cout << -1234 % 10; // -4

由此特性,所以以上代码仍然可以适用。
④. 由于本题要求“反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] ,就返回 0”,所以我们可以采用C++中的INT_MAX,INT_MIN来做判断。因为int占4字节32位,根据二进制编码的规则,INT_MAX = 2^31-1,INT_MIN= -2^31.

有了以上的理论基础,我们就可以来实战此题了:

class Solution {
public:
    int reverse(int x) {
        long long r = 0; // r 为最终的目标反转结果,初始值必须赋为0,这是为了能够从0开始加和
        // 注意这里采用了long long,所以不需要考虑溢出的情况,当数足够大时,r是有可能溢出的
        while(x) { // 当x被除为0后,退出循环
            r = r * 10 + x % 10; // 将取出来的个位放在首位上
            x /= 10; // 由于x的个位已经用完了,所以将其去掉
        }
        if (r > INT_MAX) return 0; 
        // r > INT_MAX或小于int的最小范围都返回0
        if (r < INT_MIN ) return 0;
        return r; // 返回对应的值
    }
};

不使用 long long 的思路:(不使用long long就要考虑正负溢出的情况),因为本题中有“假设环境不允许存储 64 位整数(有符号或无符号)”,所以不建议使用long long,但反转后 r 可能会非常小或非常大,那么怎么解决 r 超出int范围呢?
其实就是在循环中就去判断r是否溢出即可(因为这个数字在循环过程中随时可能超出范围),相对的,long long可以保证在循环完之后再判断,不用自己写很多判断溢出的条件。
解决溢出的方式】如何确定溢出的条件:(当然应该要在开始切割前就判断!!!)
i. 当 r > 0 时, r会大到溢出:
r * 10 + x % 10 > INT_MAX ------> r > (INT_MAX - x % 10) / 10 (溢出条件)
ii. 当 r < 0 时, r会小到溢出:
r * 10 + x % 10 < INT_MIN ------> r < (INT_MIN - x * 10) / 10 (这个条件是可以逻辑成立的,因为一个极小的负数减去一个负数是会变大的) .

class Solution {
public:
    int reverse(int x) {
        int r = 0;
        while(x) {
        	// 细心的同学可以看出,以下两个if语句是可以合并的,但是为代码的可读性,我们还是选组分正负讨论
            if(r > 0 && (r > (INT_MAX - x % 10) / 10)) return 0; // 判断 r 反转后太大溢出的情况
            if(r < 0 && (r < (INT_MIN - x % 10) / 10)) return 0; // 判断 r 反转后太小溢出的情况
            r = r * 10 + x % 10;
            x /= 10; 
        }
        // 由于在循环已经判断溢出的情况,所以跳出循环后不需要再判断了
        return r;
    }
};

(2)Leetcode8:字符串转换整数 (atoi)

第一步,先解决空格的问题,同时因为空格是占位的,我们不能单只将其trim掉,还要在去掉的同时,计算其到底占了几位。
第二步,处理完了空格,就是正负号的问题,如果题目给了正负,我们才拿,所以不能单纯的if…else…,因为还有第三种情况:题目可能还没给正负号。与空格一样,正负也要计算占位。
第三步就是正式地获取数字了,本题与上题较为类型,上题是再int这一类型上操作,而本题则是字符串上处理,但两者本质都是“累加”,int是从低位到高位,而string则是从高位到低位
what’s more, 本题也假设了只有32位的有符号整数的存储环境,所以我们分了使用long long类型存储和使用int存储的两种情况,基本思路相同,只是int的情况要加一些特判而已。

long long代码实例:

class Solution {
public:
    int myAtoi(string s) {
        int k = 0;
        // 解决空格问题
        while(k < s.size() && s[k] == ' ') ++ k; // 注意c++中没有trim函数
        if(k == s.size()) return 0; // 防止整个字符串都是空格

        // 解决正负号,1和-1可以在最后的时候乘以目标结果
        int minus = 1;
        if(s[k] == '-') minus = -1, ++ k; // 因为数组下标从0开始,所以可以s[k]
        else if(s[k] == '+') ++ k; 

        long long res = 0;
        // 正式开始寻找数字
        while(k < s.size() && s[k] >= '0' && s[k] <= '9') {
            res = res * 10 + (s[k] - '0'); // 从高位开始,和上题不同的是,上题是从低位开始的
            ++ k;
            // 这里有个优化,没有想到,就是当res已经非常大了,那么此时就可以跳出循环了
            // 此时res一定是正的
            if(res > INT_MAX) break;
        }
        res *= minus; // 最后确定符号
        if(res > INT_MAX) return INT_MAX;
        if(res < INT_MIN) return INT_MIN;
        return res;
    }
};

int代码实例:特判也和上题一致,做一个简单的左右变形即可,让其不会超出32位的范围
且由于没有long long的存储,所以不建议再到循环外去return INT_MAX/INT_MIN了,必须在循环内就即时判断

class Solution {
public:
    int myAtoi(string s) {
        int k = 0;
        // 解决空格问题
        while(k < s.size() && s[k] == ' ') ++ k; // 注意c++中没有trim函数
        if(k == s.size()) return 0; // 防止整个字符串都是空格

        // 解决正负号,1和-1可以在最后的时候乘以目标结果
        int minus = 1;
        if(s[k] == '-') minus = -1, ++ k; // 因为数组下标从0开始,所以可以s[k]
        else if(s[k] == '+') ++ k; 

        int res = 0;
        // 正式开始寻找数字
        while(k < s.size() && s[k] >= '0' && s[k] <= '9') {
            int x = s[k] - '0';// 由于现在很多地方都会用到它,所以用一个变量将其存起来,方便使用
            if(minus > 0 && res > (INT_MAX - x) / 10) return INT_MAX;
            if(minus < 0 && - res < (INT_MIN + x) / 10) return INT_MIN;// 由于此时真正的res已经是负的的了
            // 现在还有一个bug;
            //就是INT_MINd的绝对值比INT_MAX大1,但我们这里全都是正数计算,正数int存不下;所以在获取负无穷的数之前要特判一下:当 - res == INT_MIN的情况
            if(- res * 10 - x == INT_MIN) return INT_MIN;
            res = res * 10 + x; // 从高位开始,和上题不同的是,上题是从低位开始的
            ++ k;
        }
        res *= minus;
        return res;
    }
};

(3)Leetcode9:回文数

有了lc7的基础,我们可以很容易地将一个整数反转,但仍然是一样的问题,在反转时可能比原数大而发生溢出,所以要额外处理一下这一情况,其他思路基本和lc7一致,所以这里不予具体阐述了
数值方法

代码实例:

class Solution {
public:
    bool isPalindrome(int x) {
        int r = 0, temp = x;
        // 由于这里的x到最后还要作比较用,所以先存一下,否则被除掉了就无法进行比较了
        if(x < 0 || x % 10 == 0) return false; // 如果能够整除10,那一定也是不对称的
        else {
            while(temp) {
                if(r > (INT_MAX - temp % 10) / 10) return false; // 处理溢出的情况
                r = r * 10 + temp % 10;
                temp /= 10;
            }
            if(x == r) return true;
            else return false;
        }
    }
};

转化为字符串
当然,如果想要简单一些的思路,可以考虑将整数转化为字符串去判断,但这样的时间复杂度会增加

class Solution {
public:
    bool isPalindrome(int x) {
       if(x < 0) return false;
       string s = to_string(x); // 转化为字符串的函数
       return s == string(s.rbegin(), s.rend()); 
       // rgbegin()指向字符串的最后索引,rend指向字符串的开头,都是逆向迭代器
       // 其实就是begin和end,然后从右边开始计算
       // 注意c++创建对象的方式不只有new
    }
};

of course,还想优化的话,可以将字符串的前半部分和后半部分比较即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值