zoukankan      html  css  js  c++  java
  • 【LeetCode】数值运算(除法、乘方)

    C/C++数字范围(32位系统)

    bool0 ~ 1    // 1 字节
    char    // 1 字节
    short: -32768 ~ 32767    // 2 字节
    int: -21474836482147483647    // 4 字节
    unsigned: 04294967295    // 4 字节
    size_t: 0 ~ 4294967295    // 4 字节
    long: -21474836482147483647    // 4 字节,Linux64位下的long占 8 字节!!!
    unsigned long04294967295    // 4 字节
    long long: -92233720368547758089223372036854775807    // 8 字节
    unsigned long long018446744073709551615    // 8 字节
    __int64: -92233720368547758089223372036854775807    // 8 字节
    unsigned __int64: 018446744073709551615    // 8 字节
    float1.17549-038 ~ 3.40282e+038    // 4 字节
    double2.22507e-308 ~ 1.79769e+308    // 8 字节
    long double3.3621e-4932 ~ 1.18973e+4932    // 12 字节

    1. 整数除法

    实现整数除法,不使用 * 、/ 、% 三个运算符。若结果越界,返回 INT_MAX。

    原理:

        假设计算 15 除以 3,15 是 dividend 被除数,3 是 divisor 除数。

        “除法”需要知道 dividend 能够减去 divisor 多少次而不使 dividend 为负。

        开始计算叭~将结果记录在 result 中(初始化为0)。

        首先 15 - 3 = 12 > 0;尝试减去更大的数,把 3 左移一位 (0011)2 -> (0110)2 得到 6,15 - 6 > 0;再左移一位得到 12,15 - 12 > 0;继续左移一位得到24,15 - 24 < 0。这样我们知道 dividend (15) 最多能减 12。12 是由 divisor (3) 左移 2 次得到,左移 2 次相等于 × 4,4 即为 (0001)2 左移 2 次得到,因此将 4 加到 result 中。

        接下来 dividend 15 - 12 后剩余 3,重复上述计算,将 1 加到 result 中得到最终答案 5。

        被除数和除数若含有负数,则统一化为正数按上述算法计算后,再判断 result 符号即可。

     

    C++实现:

     1 int divide(int dividend, int divisor) {
     2     // 整数除法,因此只有当 dividend == -231,divisor == -1 时结果才会越界
     3     if (!divisor || (dividend == INT_MIN && divisor == -1))
     4         return INT_MAX;
     5     /* !使用 异或 操作符判断符号值得学习! */
     6     int sign = ((dividend < 0) ^ (divisor < 0)) ? -1 : 1;
     7     // dividend == -231时,取绝对值后仍赋值给 int 则会越界,因此存储在 long long
     8     long long dvd = labs(dividend);
     9     long long dvs = labs(divisor);
    10     int res = 0;
    11     while (dvd >= dvs) { 
    12         long long temp = dvs, multiple = 1;
    13         while (dvd >= (temp << 1)) {
    14             temp <<= 1;
    15             multiple <<= 1;
    16         }
    17         dvd -= temp;
    18         res += multiple;
    19     }
    20     return sign == 1 ? res : -res; 
    21 }

    2. 乘方

    实现 Pow(x, n)。

     

    若用类似于求 n 的阶乘的递归方法计算,时间复杂度为 O(n)。

    但考虑到 n 个 x 相乘式子的对称关系,可以采用二分法,xn = xn/2 * xn/2 * xn%2,时间复杂度为 O(logn)。

    C++实现:

    1 double myPow(double x, int n) {
    2     if (!n)
    3         return 1;
    4     if (n < 0) {
    5         n = -n;
    6         x = 1 / x;
    7     }
    8     return (n % 2 == 0) ? myPow(x * x, n / 2) : x * myPow(x * x, n / 2);
    9 }

        但这段代码并没有做边界条件判断,n 取值 INT_MIN 时,-n 并不是 INT_MAX

    需要在 n < 0 的条件下增加判断

    if (n == INT_MIN)
        return 1 / (x * myPow(x, INT_MAX));

    或者改为

    if (n < 0) {
        return 1 / (x * myPow(x, -(n + 1)));
    }

     最后可以用位运算加快执行速度

    1 double myPow(double x, int n) {
    2     if (!n)
    3         return 1;
    4     if (n < 0) {
    5         return 1 / (x * myPow(x, ~n));
    6     }
    7     return ((n & 1) == 0) ? myPow(x * x, n >> 1) : x * myPow(x * x, n >> 1);
    8 }

    用 ~n 代替 -(n + 1)

    e.g. n = 15,求 ~n。

     n的源码 = 00000000  00000000  00000000  00001111
    正数的补码是自身(计算机以补码存储数据)
     n的补码 = 00000000  00000000  00000000  00001111
    按位取反(包括符号位)
    ~n的补码 = 11111111  11111111  11111111  11110000
    负数的补码是源码取反加1,源码也是补码取反加1(符号位不变)
    ~n的源码 = 10000000  00000000  00000000  00010000

    求得 ~n 为 -16。同理 n = -15 时,求得 ~n = 14。

     

    if ((n & 1) == 0) 判断奇偶效率更高,虽然编译器都会将 n % 2 优化为位运算。

    但值得注意的是,& 作为“按位与”而不是“取地址符”时,其优先级低于 == 和 !=,因此如果不写括号的话 (n & 1 == 0) 为永 false 的。

    编写更加易读的代码比起过多考虑细枝末节的效率问题更加重要!若想高效,建议改进算法,而不是改进写法!

     

    递归虽然很好理解,但会占用递归栈的空间,因此用迭代的方法更加高效。

    考虑 n 的二进制表示,例如 n = (1000 1011)2,那么xn = x1+2+8+128 = x1 * x2 * x8 * x128

    因此可以循环 n 的每一位,如果该位置是 1,就把 xi 乘到结果中去,时间复杂度为 O(logn)。

    任何数与 1 相与结果不是 0 就是 1,一般用 n & 1 判断 n 的奇偶,这里则是不断把 n 右移一位,判断 n 的最后一位是不是 1,如果是 1,就把 xi 乘到结果中去。

     1 double myPow(double x, int n) {
     2     if (!n)
     3         return 1;
     4     if (n < 0) {
     5         return 1 / (x * myPow(x, ~n));
     6     }
     7     double result = 1;  
     8     for(; n > 0; x *= x, n >>= 1) {  
     9          if (n & 1 > 0)  
    10             result *= x;  
    11     }  
    12     return result;
    13 }

    3. 

  • 相关阅读:
    03-树3 Tree Traversals Again
    Utuntu下Xshell使用+vi使用
    CSDN总结的面试中的十大算法
    EDM(邮件营销)
    腾讯CDC谈扁平化设计
    Graph Search图谱搜索
    LBS 与 GPS 定位之间的区别
    中间件的理解
    夏梦竹谈Hive vs. HBase的区别
    维基百科上—数据仓库、数据挖掘、OLAP三者之间的区别
  • 原文地址:https://www.cnblogs.com/wayne793377164/p/7159273.html
Copyright © 2011-2022 走看看