leetcode经典题目(5)--数学问题

    技术2022-07-11  117

    1 计数质数

    题目描述:统计所有小于非负整数 n 的质数的数量。 解题思路:如果遍历所有数值,判断每个数是否为质数,就会超时。这里使用埃拉托斯特尼筛法,每次找到一个素数时,将能被素数整除的数排除掉。使用一个数组isPrime,isPrime[i]表示是否为质数。如果找到一个质数,则将数组中该数的所有小于n的倍数位置的值置为false。

    class Solution { public: int countPrimes(int n) { if (n <= 1) return 0; vector<bool> isPrime(n,true); isPrime[0] = false; isPrime[1] = false; int count = 0; for (int i = 2; i < n; i++){ if (isPrime[i]){//i是素数 count++; for (int j = 2 * i; j < n; j += i) isPrime[j] = false; } } return count; } };

    2 最大公约数

    2.1 辗转相除法
    int gys(int a, int b){ if (a < b){ int tmp = b; b = a; a = b; } while (b != 0){ int tmp = a % b; a = b; b = tmp; } return a; }

    两个数的最小公倍数等于两数乘积除以最大公约数。

    2.2 使用位操作和减法求解最大公约数
    bool isEven(int a){ return (a & 1) == 0; } int gcd(int a, int b){ if (a < b) return gcd(b,a); if (b == 0) return a; if (isEven(a) && isEven(b)) return 2 * gcd(a >> 1, b >> 1); else if (isEven(a) && !isEven(b)) return gcd(a >> 1, b); else if (!isEven(a) && isEven(b)) return gcd(a, b >> 1); else return gcd(b, a - b); }

    3. 进制转换

    3.1 7 进制(NO.504)
    class Solution { public: string convertToBase7(int num) { if (num < 0) return '-' + convertToBase7(-1*num); if (num == 0) return "0"; string res; while (num != 0){ res.insert(res.begin(),num % 7 + 48); num = num / 7; } return res; } };
    3.2 16 进制(NO.405)

    题目描述:给定一个整数,编写一个算法将这个数转换为十六进制数。对于负整数,我们通常使用 补码运算 方法。 暴力解法:对于正数,直接对16求余数和商。对于负数,采用补码,首先求绝对值的16进制表示(注意位数不够时前面要补0),在对所有位取反得到反码,将最后一位加1得到补码。

    class Solution { public: string toHex(int num) { if (num == 0) return "0"; if (num < 0){ string tmp = toHex(-1*num); while (tmp.size() < 8)//前面添加0补位 tmp.insert(tmp.begin(),'0'); for (char& c : tmp){//取反,得到反码 if ((c >= 'a' && c <= 'f') || (c >= '0' && c <= '5')) c = 150 - c; else c = 111 - c; } //末尾加1 int key = 1; for (int i = tmp.size() - 1; i >= 0; i--){ if (key == 1){ if (tmp[i] == 57){ tmp[i] = 'a'; key = 0; } if (tmp[i] == 'f') tmp[i] = '0'; else{ tmp[i] += key; key = 0; } } } return tmp; } string res;//处理正数 while(num != 0){ int r = num % 16; if (r < 10) res.insert(res.begin(),r + 48); else res.insert(res.begin(),r + 87); num = num / 16; } return res; } };

    核心思想:使用位运算,每4位,对应1位16进制数字。 (1)使用0xf(00…01111b)获取num的低4位。0x开头的数字是十六进制数字,后面跟的数字都要按照十六进制理解,0xf就是十六进制数字f,转换为10进制数字就是15. (2)">>"算数位移,其中正数右移左边补0,负数右移左边补1。位移运算并不能保证num==0,需要使用32位int保证(对应16进制小于等于8位)。如果num是正数,右移可以使num变为0,如果num是负数,右移会往前面添加1,所以右移num不会变为0.

    class Solution { public: string toHex(int num) { if (num == 0) return "0"; string str = "0123456789abcdef"; string res; while (num && res.size() < 8){ res.insert(res.begin(),str[num & 0xf]); num >>= 4; } return res; } };
    3.3 26进制-Excel表列名称(NO.168)

    题目描述:给定一个正整数,返回它在 Excel 表中相对应的列名称。例如, 1 -> A … 26 -> Z 27 -> AA 28 -> AB … 解题思路:要注意26与Z对应,而n&=0。

    class Solution { public: string convertToTitle(int n) { if (n == 0) return ""; string res; while (n){ if (n % 26 != 0){ res.insert(res.begin(),n % 26 + 64); n = n / 26; } else{//n是26的整数倍 res.insert(res.begin(),'Z'); n = n / 26 - 1; } } return res; } };

    4. 阶乘

    4.1 统计阶乘尾部有多少个 0(NO.172)

    题目描述:给定一个整数 n,返回 n! 结果尾数中零的数量。 解题思路:尾部的 0 由 2 * 5 得来,2 的数量明显多于 5 的数量,因此只要统计有多少个 5 即可。对于一个数 N,它所包含 5 的个数为: N / 5 + N / 5 2 + N / 5 3 + . . . N/5 + N/5^2 + N/5^3 + ... N/5+N/52+N/53+...,其中 N/5 表示不大于 N 的数中 5 的倍数贡献一个 5, N / 5 2 N/5^2 N/52 表示不大于 N 的数中 5 2 5^2 52 的倍数再贡献一个 5 。例如25贡献两个5,在5的倍数里面算了一次,在25的倍数里面又算了一次,共算了两次。

    class Solution { public: int trailingZeroes(int n) { int count = 0; while(n){ count += n / 5; n /= 5; } return count; } };

    5. 字符串加法减法

    5.1 二进制加法(NO.67)

    题目描述:给你两个二进制字符串,返回它们的和(用二进制表示)。输入为 非空 字符串且只包含数字 1 和 0。

    class Solution { public: string addBinary(string a, string b) { //首先将两个字符串的位数补齐 int maxSize = a.size() < b.size() ? b.size() : a.size(); while (a.size() < maxSize) a.insert(a.begin(),'0'); while (b.size() < maxSize) b.insert(b.begin(),'0'); int key = 0;//表示是否进位 string res; for (int i = a.size()-1; i >= 0; i--){ int tmp = a[i] - '0' + b[i] - '0' + key; if (tmp > 1){ res.insert(res.begin(),'0' + tmp -2); key = 1; } else{ res.insert(res.begin(),'0' + tmp); key = 0; } } if (key == 1) res.insert(res.begin(),'1'); return res; } };
    5.2 字符串加法(NO.415)

    题目描述:给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。

    class Solution { public: string addStrings(string num1, string num2) { int key = 0; int i = num1.size() - 1, j = num2.size() - 1; string res; while (i >= 0 || j >= 0 || key == 1){ int a = 0, b = 0; if (i >= 0){ a = num1[i] - '0'; i--; } if (j >= 0){ b = num2[j] - '0'; j--; } int tmp = a + b + key; if (tmp > 9){ tmp -= 10; key = 1; } else{ key = 0; } res.insert(res.begin(),tmp + '0'); } return res; } };

    6. 相遇问题

    6.1 改变数组元素使所有的数组元素都相等(NO.462)

    题目描述:给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1。 您可以假设数组的长度最多为10000。 解题思路:这是个典型的相遇问题,移动距离最小的方式是所有元素都移动到中位数。理由如下:设 m 为中位数。a 和 b 是 m 两边的两个元素,且 b > a。要使 a 和 b 相等,它们总共移动的次数为 b - a,这个值等于 (b - m) + (m - a),也就是把这两个数移动到中位数的移动次数。

    class Solution { public: int minMoves2(vector<int>& nums) { sort(nums.begin(),nums.end()); int count = 0; int i = 0, j = nums.size() - 1; while (i <= j){ count += nums[j] - nums[i]; i++; j--; } return count; } };

    7. 多数投票问题

    7.1 数组中出现次数多于 n / 2 的元素

    题目描述:给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。 解法一:由于多数元素必存在,对原数组排序后,n/2位置的元素就是多数元素。

    class Solution { public: int majorityElement(vector<int>& nums) { sort(nums.begin(),nums.end()); return nums[nums.size()/2]; } };

    解法二:

    8. 其他

    8.1 平方数(NO.367)

    题目描述: 给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。 解题思路:平方序列:1,4,9,16,…,间隔:3,5,7,…。间隔为等差数列,使用这个特性可以得到从 1 开始的平方序列。

    class Solution { public: bool isPerfectSquare(int num) { int tmp = 1; while (num > 0){ num -= tmp; tmp += 2; } if (num == 0) return true; else return false; } };
    8.2 3 的 n 次方(NO.326)

    题目描述:定一个整数,写一个函数来判断它是否是 3 的幂次方。 解题思路:

    class Solution { public: bool isPowerOfThree(int n) { int tmp = 1; while(tmp < n){ tmp *= 3;//可能会超出int型范围 } return tmp == n; } }; class Solution { public: bool isPowerOfThree(int n) { return n > 0 && (1162261467 % n == 0); } };
    8.3 除自身以外数组的乘积(NO.238)

    题目描述:给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。请不要使用除法,且在 O(n) 时间复杂度内完成此题。 解题思路:如果可以使用除法,则可求出数组中所有数字的乘积,再除以相应位置的元素。使用left[i]表示第i个数前面所有数的乘积。right[i]表示第i个数后面所有数的乘积。

    class Solution { public: vector<int> productExceptSelf(vector<int>& nums) { int n = nums.size(); vector<int> left(n,1); vector<int> right(n,1); for (int i = 1; i < n; i++) left[i] = left[i-1] * nums[i-1]; for (int i = n - 2; i >= 0; i--) right[i] = right[i+1] * nums[i+1]; vector<int> product(n,1); for (int i = 0; i < n; i++) product[i] = left[i] * right[i]; return product; } }; class Solution { public: vector<int> productExceptSelf(vector<int>& nums) { int n = nums.size(); vector<int> product(n,1); int left = 1; for (int i = 1; i < n; i++){ left *= nums[i-1]; product[i] *= left; } int right = 1; for (int i = n - 2; i >= 0; i--){ right *= nums[i+1]; product[i] *= right; } return product; } };
    8.4 三个数的最大乘积(NO.628)

    题目描述:给定一个整型数组,在数组中找出由三个数组成的最大乘积,并输出这个乘积。 解题思路:数组中三个数的最大乘积,可能是最大的三个数的乘积(三个正数),也可能是最大数和最小的两个数的乘积(一正两负)。

    class Solution { public: int maximumProduct(vector<int>& nums) { int max1 = INT_MIN, max2 = INT_MIN, max3 = INT_MIN, min1 = INT_MAX, min2 = INT_MAX; for (int n : nums){ if (n > max1){ max3 = max2; max2 = max1; max1 = n; } else if (n > max2){ max3 = max2; max2 = n; } else if (n > max3) max3 = n; if (n < min1){ min2 = min1; min1 = n; } else if (n < min2) min2 = n; } return max(max1*max2*max3,max1*min1*min2); } }; class Solution { public: int maximumProduct(vector<int>& nums) { int n = nums.size(); sort(nums.begin(),nums.end()); return max(nums[n-1]*nums[n-2]*nums[n-3],nums[n-1]*nums[0]*nums[1]); } };
    Processed: 0.015, SQL: 9