Leetcode题解系列——Divide Two Integers(c++版)

题目链接:29. Divide Two Integers

题目大意:给出两个int类型的除数与被除数,求出它们的商,但是不能使用乘法,除法,求模等方法

注意点:
1. int类型整数的范围,除法过程会出现越界的情况
2. 对于正数负数的处理
2. 除数为0的情况
3. 不能使用乘除法

一. 第一想法

不使用乘除法来计算除法的商,那就意味着我们只能使用加减法以及位运算。将除法变成减法来处理,这是我的第一个想法,将被除数一直减去除数,直到被除数小于除数,返回减法的次数即可

对于当除数或者被除数出现负数的情况时,我们不能直接进行减法。必须将除数与被除数取绝对值后才能继续运算。

越界情况的处理,由于一个int类型的数的范围为 -2^31 ~ 2^31-1;所以当最小负数除以-1的情况下,会出现越界情况,必须单独处理

当然,除数为0的时候也要单独处理

根据上述思路,我写出了第一份错误的代码

class Solution {
public:
    int divide(int dividend, int divisor) {
        //处理越界
        if(divisor == 0 || (divisor == -1 && dividend == INT_MIN)) return INT_MAX;
        //处理负数
        bool is_neg = 0;
        if((dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0)){
            is_neg = 1;
        }
        int result = 0;
        //取绝对值
        int did = abs(dividend);
        int dir = abs(divisor);
        //进行减法
        while(did >= dir){
            did -= dir;
            result++;
        }
        return is_neg?-result:result;
    }
};

二.分析错误并更换思路

首先,简单的运算已经可以正确通过,如7/3之类的。但是当出现-2147483647,1的情况时,输出答案却是0,这显然与我们的想法背道而驰。

简单的debug后,发现是c++库中的abs函数无法将-2147483647成功转化为一个正数,这就需要查阅一下这个问题出现在哪。于是,我需要自己实现一个abs函数来实现上述功能

unsigned int __abs(int value)
{
    unsigned int copyed_value = value;
    return (copyed_value > 0x80000000) ? -value : copyed_value;
}

分析:
因为32位下最小的int值为0x80000000,最高位符号位为1.当位对位拷贝到unsigned int中时,仍然是这个值.但其他的负数除了最高位为1外,其余位置也有值,比如-1的16进制表示为:0x80000001.所以,我们先执行位对位的拷贝,到copyed_value中.所以出现了判断情况:

(1)如果copyed_value是大于0x80000000的,说明value是负数,所以我们直接取相反数(-value);

(2)如果copyed_value是等于0x80000000的,说明value恰好是最小的那个负数,执行位对位拷贝后,copyed_value中存放的就是value的绝对值,所以返回copyed_value;

(3)如果copyed_value是小于0x80000000,说明value为正数.直接返回value或者copyed_value即可。

方法来源自 c++自己实现abs

经过修改后的代码:

class Solution {
public:
    int divide(int dividend, int divisor) {
        if(divisor == 0 || (divisor == -1 && dividend == INT_MIN)) return INT_MAX;
        bool is_neg = 0;
        if((dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0)){
            is_neg = 1;
        }
        int result = 0;
        unsigned int did = __abs(dividend);
        unsigned int dir = __abs(divisor);
        while(did >= dir){
            did -= dir;
            result++;
        }
        return is_neg?-result:result;
    }

    unsigned int __abs(int value)
    {
        unsigned int copyed_value = value;
        return (copyed_value > 0x80000000) ? -value : copyed_value;
    }
};

但是这样仍然无法通过测试,原因在于时间复杂度太高,出现了time limited错误。
这时候需要分析一下,我们能不能优化一下这个减法步骤,因为当被除数很大时,它所减法的步骤可能也很大,时间复杂度为O(n)。

我们用日常除法的思路思考一下,我们平常也不会一项一项的减,而是先找到除数较大的倍数的一个数与被除数先比较,然后再减去较小的倍数,直到无法继续减。这样的想法能大大减少除法的次数,时间复杂度为O(logn)

三. 修改代码

class Solution {
public:
    int divide(int dividend, int divisor) {
        //处理溢出情况
        if(divisor == 0 || (divisor == -1 && dividend == INT_MIN)) return INT_MAX;
        bool is_neg = 0;
        //处理负数
        if((dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0)){
            is_neg = 1;
        }
        int result = 0;
        unsigned int did = __abs(dividend);
        unsigned int dir = __abs(divisor);
        //减少减法的次数
        while(did >= dir){
            long temp = dir;
            long mul = 1;
            //找出除数倍数最大的数,但是小于被除数
            while(did >= temp<<1){
                temp = temp << 1;
                mul = mul << 1;
            }
            did -= temp;
            result += mul;
        }
        return is_neg?-result:result;
    }
    //绝对值函数
    unsigned int __abs(int value)
    {
        unsigned int copyed_value = value;
        return (copyed_value > 0x80000000) ? -value : copyed_value;
    }
};

accepted!
Leetcode题解系列——Divide Two Integers(c++版)_第1张图片

四.总结

这题的思路还是比较清晰的,但是不用除法乘法来求商也是比较新颖,第一次做。如何分析关键步骤的复杂度十分关键,如何用算法思想来减少时间复杂度,先减去最大的,然后次之,直至减不了,有点贪心算法的味道。

除此之外,通过这道题还学到了abs的问题以及如何自己写一个abs函数。

你可能感兴趣的:(Leetcode题解)