算法(八)数学

leetcode

69. x 的平方根

题目

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4
输出: 2
示例 2:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 
     由于返回类型是整数,小数部分将被舍去。

题解1

牛顿法解决问题。令 f ( x ) = x 2 − a f(x)=x^2-a f(x)=x2a,设立初始值 x 0 x_0 x0,在 x 0 x_0 x0处的切线斜率为 f ′ ( x 0 ) f'(x_0) f(x0),切线与x轴的交点为 x 1 x_1 x1 x 1 x_1 x1要比 x 0 x_0 x0更接近 f ( x ) f(x) f(x)的0值点,因而不断迭代的过程中 x i x_i xi的取值不断接近0值点,直到 x i x_i xi x i + 1 x_{i+1} xi+1差异很小,就意味着 x i x_i xi已经接近0值点了。

在这里插入图片描述
示例代码如下:

class Solution {
public:
    int mySqrt(int x) {
        if (x <= 0) {
            return 0;
        }

        double C = x; // 需要设置为double,因为float可能精度不够
        double x0 = x;
        while (true) {
            double xi = 0.5 * (x0 + C / x0);
            if (fabs(x0 - xi) < eps) {
                break;
            }
            x0 = xi;
        }

        return int(x0);
    }
private:
    double eps = 1e-8;
};

题解2

二分法解决问题。示例代码如下:

int mySqrt(int x) {
    if (x == 0) {
        return 0;
    }
    int low = 1;
    int high = x;
    while (low < high) {
        int mid = low + (high - low) / 2; // 写成(low + high) / 2可能会有溢出风险
        // 当mid == x / mid的时候,
        // 因为x / mid < 真实值(有int取整),
        // 因而当mid == x / mid 的时候mid并不一定符合要求, 需要再往右找一找
        if (mid <= x / mid) {
            low = mid + 1;
        // 当mid > x / mid的时候,mid有可能符合要求,
        // 因而high不能等于mid-1,而要等于mid
        } else {
            high = mid;
        }
    }
    // low本身有可能符合要求,
    // 因为正好mid能够取整且刚好low=mid=high
    if (x / low == low) {
        return low;
    }
    // 也有可能跳出时,low = mid + 1, 
    // 而mid是符合要求的,
    // 因而需要返回low-1
    return low - 1;
}

415. 字符串相加

题目

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例 1:
输入:num1 = "11", num2 = "123"
输出:"134"

示例 2:
输入:num1 = "456", num2 = "77"
输出:"533"

示例 3:
输入:num1 = "0", num2 = "0"
输出:"0"

题解

从后向前进行按位相加,示例代码如下:

class Solution {
public:
    string addStrings(string s, string t) {
        string s_a = s;
        if (s_a.size() < t.size()) {
            string temp = s_a;
            s_a = t;
            t = temp;
        }
        int len1 = s_a.size();
        int len2 = t.size();
        int num1 = 0;
        int num2 = 0;
        int flag = 0;
        int sum = 0;
        while (len1 > 0) {
            num1 = s_a[len1 - 1] - '0';
            if (len2 > 0) {
                num2 = t[len2 - 1] - '0';
            } else {
                num2 = 0;
            }
            sum = num1 + num2 + flag;
            s_a[len1 - 1] = sum % 10 + '0';
            flag = sum / 10;
            --len1;
            --len2;
        }
        if (flag == 1) {
            s_a = "1" + s_a;
        }
        
        return s_a;
    }
};

461. 汉明距离

题目

两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。给出两个整数 x 和 y,计算它们之间的汉明距离。注意:0 ≤ x, y < 231。

示例:
输入: x = 1, y = 4
输出: 2
解释:
1   (0 0 0 1)
4   (0 1 0 0)
       ↑   ↑

上面的箭头指出了对应二进制位不同的位置。

题解

最直接异或思路解决问题。示例代码如下所示:

class Solution {
public:
    int hammingDistance(int x, int y) {
        int m = x ^ y;
        int count = 0;
        while (m) {
            count += m & 1;
            m /= 2;
        }

        return count;
    }
};

复杂度

时间复杂度: O ( l o g n ) O(logn) O(logn)
空间复杂度: O ( 1 ) O(1) O(1)

野路子

筛选N以内素数

题目

如题

题解

判断i是否为素数,只需要判断3到 i \sqrt{i} i 之间的数值是否能整除i即可,示例代码如下所示:

// Online C++ compiler to run C++ program online
#include <iostream>
#include <cmath>
#include <vector>

using namespace std;

int main() {
    vector<int> res;
    int N = 50;
    for (int i = 3; i <= N; i += 2) {
        bool flag = true;
        for (int j = 3; j <= sqrt(i); j += 2) {
            if (!(i % j)) {
                flag = false;
            }
        }
        if (flag) {
            res.emplace_back(i);
        }
    }

    for (auto& elem: res) {
        cout << elem << endl;
    }

    return 0;
}

大数相乘

模拟乘法的实现过程,有如下示例代码:

void multiply(const std::vector<int>& a, const std::vector<int>& b, std::vector<int>& result) {
    if (a.empty() || b.empty()) {
        return;
    }
    
    int a_size = a.size();
    int b_size = b.size();
    result.reserve(a_size + b_size);
    
    for (int i = 0; i < a_size; ++i) {
        for (int j = 0; j < b_size; ++j) {
            result[i + j] += a[i] * b[j];
        }
    }
    
    for (int i = 1; i < a_size + b_size; ++i) {
        result[i] += result[i - 1] / 10;
        result[i - 1] %= 10;
    }
    
    int high = a_size + b_size - 1;
    while (result[high] == 0) {
        --high;
    }
    
    auto tmp_result = vector<int>(result.begin(), result.begin() + high + 1);
    result = tmp_result;
}

加一(leetcode_66)

题目

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
示例 2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。
示例 3:

输入:digits = [0]
输出:[1]

题解

正常大数相加的思路,示例代码如下所示:

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int length = digits.size();
        if (length == 0) {
            return digits;
        }

        reverse(digits.begin(), digits.end());
        int carry = 0;
        for (int i = 0; i < length; ++i) {
            int sum = digits[i] + carry;
            if (i == 0) {
                sum += 1;
            }
            digits[i] = sum % 10;
            carry = sum / 10;
        }
        if (carry == 1) {
            digits.emplace_back(carry);
        }

        reverse(digits.begin(), digits.end());

        return digits;
    }
};

Pow(x, n)(leetcode_50)

题目

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。

示例 1:
输入:x = 2.00000, n = 10
输出:1024.00000

题解

二分法递归解决问题,示例代码如下所示:

class Solution {
public:
    double myPow(double x, int n) {
        long long N = n;
        return n > 0 ? multiply(x, N) : 1.0 / multiply(x, -N);
    }

    double multiply(double x, long long N) {
        if (N == 0) {
            return 1.0;
        }
        double y = multiply(x, N / 2);
        return N % 2 == 0 ? y * y : y * y * x; // 奇偶之分
    }
};

复杂度

时间:O(logn),二分的时间复杂度
空间:O(logn),递归栈的空间复杂度

排列硬币(leetcode_441)

题目

你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。给定一个数字 n,找出可形成完整阶梯行的总行数。n 是一个非负整数,并且在32位有符号整型的范围内。

示例 1:
n = 5
硬币可排列成以下几行:
¤
¤ ¤
¤ ¤
因为第三行不完整,所以返回2.

示例 2:
n = 8
硬币可排列成以下几行:
¤
¤ ¤
¤ ¤ ¤
¤ ¤
因为第四行不完整,所以返回3.

题解

累积相减,示例代码如下所示:

class Solution {
public:
    int arrangeCoins(int n) {
        int i = 1;
        for (; i <= n; ++i) {
            n -= i;
        }

        return i - 1;
    }
};

复杂度

时间:O(n)
空间:O(n)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值