[LeetCode刷题笔记]Math数学类型题目(一)重写基本运算符

原创文章

转载请注册来源http://blog.csdn.net/tostq

      个人觉得Math类的题最为经典的,就是重写各类常见的运算,比如乘法、除法、加法、平方、开方之类,题目都不难,但要求做题者要有一定的数学知识,及对计算机计算结构有所了解。
      这一类题的主要难点:
      1、一般不能使用+、-、*、/的符号,比如不让使用+号做加法运算
      2、注意数据的溢出,比如最小整数-2147483648的绝对值会溢出
      3、计算速度要快
      4、注意输入数(一般是整数)的符号
      一般的解题思路:
      1、通过>>、<<进位符号
      2、减少重复循环
      2、避免数据溢出INT_MAX=0x7fffffff
      3、利用二分法来解决未知变量
      下面给出几题的例子。

一、加减法的重写
      371. Sum of Two Integers
      这个题目要求计算两个整数的加法,却要求你不能使用+或-法
      不用+或-来实现加法,难道还用乘法?刚看完这题我是一脸懵逼的.jpg
      但是仔细一想,应该是通过二进制的与或非来求解的。

      另外作为本科学EE的我来说,突然想起了,曾经还做过通过与非门构建全加器的实验,所以不难发现:
      比如13(01101)+25(11001)=100110
      13&25=01001表示的是进位
      13^25=10100表示的是半加法
      半加+进位<<1就得到了最后结果,所以我们可以通过迭代从而有如下解法

class Solution {
public:
    int getSum(int a, int b) {
        return a & b ? getSum((a & b) << 1, a ^ b) : a | b;
    }
};
      当进位为零的时候,就可以直接通过或来得到加法(异或也是一样的)


二、乘除法的重写
      29. Divide Two Integers
      这一个题目是求两个整数的除法,也就是重写除法符号/
      可能我们第一眼看过去,可以通过不停的减法,就可以完成除法了。
      比如5/2=... 5-2=3, 3-2=1<2, 所以5/2=2;
      但是这种将减法来代替除法的方法,会消耗大量的时间
      运行时会提出Time Limit Error
      所以正确的方法是通过>> 这个右移命令,其表示/2
      比如9=2*4+1,所以9/2=4.

class Solution {
public:
    int divide(int dividend, int divisor) {
        if(divisor==0||(dividend == INT_MIN && divisor == -1))return INT_MAX;        
        int sign=((dividend>=0&&divisor>=0)||(dividend<=0&&divisor<=0))?1:-1;
        long long dvd = labs(dividend);
        long long dvs = labs(divisor);
        int res=0;
        while(dvd>=dvs){
            long long temp=dvs;
            int r=1;
            while(dvd>=(temp<<1)){
                temp=temp<<1;
                r=r<<1;
            }
            dvd=dvd-temp;
            res=res+r;
        }
        return sign*res;
    }
};
       这里我们可以看到long long的数据结构,我们将输入的数据变成了长整型,为什么要这么做呢?
       原因是当输入为-2147483648时,它的绝对值为2147483648,这是超过整数范围的-2147483648~2147483647的,所以需要把其绝对值变成Long long。
       另外temp变为Long 还有另一层意思,temp需要乘以2,
       TIPS:
       对于做加法、乘法或平方之类变量,一定要观察其会不会出现数据溢出
       所以这一题的难点在于
       1、对数据溢出的处理
       2、理解除法运算在计算机中的实现
       同理,乘法可以用<<和类似的方法来计算。

三、平方(多次方)的重写
       这类题总共有两类题:求某数的平方及判断某数是否是平方数
       1、求某数的平方
       50. Pow(x, n)
       这道题求的是x的n次方,比较直观的方式就是利用while循环,x连续相乘n次。
       这样的话总共需要循环n次,花得时间还是太长
       不过我们利用x^n=x*x^(n-1),来减少循环次数。
class Solution {
public:
    double myPow(double x, int n) {
        double res=1;
        x = n>=0?x:1/x;
        unsigned int nl = abs(n);
        while(nl){
            if (nl&1) res*=x;
            nl>>=1;
            x*=x;
        }
        return res;
    }
};
      这里我们并不需要考虑是否会出现整型溢出的情况,因为n不会太大。
      不过这里定义unsigned int的原因是因为我们通过n1&1来判断当前位是否为0,而当n1为负数时,其首位为1,这样会影响我们判断。
      另外还有一种方法是通过递归方式,算法思路是相同,不过更加直观。
double myPow(double x, int n) {
    x= n>=0?x:(1./x);
    unsigned int temp= n>=0?n:(-n);
    if(temp==0)
        return 1;
    
    double t=myPow(x,temp>>1);
    if(n&1){
        return x*t*t;
    }else{
        return t*t;
    }
}
       2、判断是否是某数的平方
       231. Power of Two
       该题判断某数是否是2的平方
       通过while来不停地除以2是比较直观的方法,比如这样。
class Solution {
public:
    bool isPowerOfTwo(int n) {
        while(n/2>0){
            if(n%2!=0)return false;
            n=n/2;
        }
        return n==1?true:false;
    }
};
       不过更简单的方法是观察数的固有特性
       比如2^4=1000,2^5=10000,显然通过二进制,我们就可以方便地发现平方数的特性
       所以就有了如下解法
bool isPowerOfTwo(int n) {
    return (n>0)&&(!(n&(n-1)));
}
       同理关于判断某数是否是3次方数
       353. Power of Three
       3次方数,可以通过判断其能否被最大3次方数整除来确定。
    bool isPowerOfThree(int n)
    {
        int maxPowerOfThree = (int)pow(3, (int)(log(INT_MAX)/log(3)));
        return n>0 && maxPowerOfThree%n==0;
    }
      再来一例子:
      342. Power of Four
      判断某数是否是4次方数,还要求你不能使用递归和循环
      4次方,表示为4^1=100,4^2=10000……4^n=(2^n)^2
class Solution {
public:
    bool isPowerOfFour(int num) {
        int a=sqrt(num);
        return num>0 && a*a==num && !(a&(a-1));
    }
};

四、开方的重写
       同平方问题一样,开方问题也有类似两类问题:
       1、计算某数的开方
       2、判断某数是否是能开方
       不过开方相比于平方,不能可以变成数的连续相乘,所以就没有那么直观了
       开方问题需要找到x,满足x*x==n。
       同许多求解未知参数的问题一样,开方也可以通过二分法来解决。
       69. Sqrt(x)
// Binary search 
int mySqrt(int x) { // here x>0
    int low=0,high=x,mid=0;
    if(x<2)return x;
    while(low<high){
        mid=(low+high)/2;
        if(x/mid>=mid)low=mid+1;
        else high=mid;
    }
    return high-1;
}
       另外,我们需要注意到Low+high也很可能导致数据溢出
       所以比较推荐的写法是:
       mid=low+(high-low)>>1
       这里还将除2用>>1来代替,加快计算速度
       2、判断某数是否是能开方
       367. Valid Perfect Square

bool isPerfectSquare(int num) {
    if(num<2)return true;
    num=(unsigned int)num;
    unsigned int low=1,mid;
    unsigned int high=(num>>1)+1;


    while(low<high){
        mid=(low+high)>>1;


        long int sq=mid*mid;
        if(sq==num)return true;
        if(sq<num)
            low=mid+1;
        else
            high=mid-1;


    }
    return low*low==num?true:false;
}
     这里将sq变为Long int,是为了防止mid*mid溢出




     

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值