69. x 的平方根
解题思路:
- 暴力试根法
从0开始遍历,找到 i 2 > x i^2 > x i2>x即可
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
缺点:i 越大,越容易溢出
int mySqrt(int x){
if(!x)
return x;
long long i = 0;
while(i * i < x)
i++;
if(i * i == x)
return i;
return i - 1;
}
- 牛顿法
以上图片来自《牛顿法与拟牛顿法》
详细证明也参考此文章
不讨论其数学证明,直接使用牛顿法结论:
int mySqrt(int x){
if(x < 2)
return x;
double x0, x1;
x0 = x;
x1 = (x0 + x / x0) / 2.0;
while(fabs(x0 - x1) >= 1.0){
x0 = x1;
x1 = (x0 + x / x0) / 2.0;
}
return (int)x1;
}
时间复杂度: O ( log N ) \mathcal{O}(\log N) O(logN),此方法是二次收敛的。
空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)
注:C语言中求绝对值的函数有两种,分别为abs()、fabs()。abs()函数用来对整型变量求绝对值,fabs()函数用来对浮点型变量求绝对值。abs()属于头文件“stdlib.h”,fabs()属于头文件“math.h”。
172. 阶乘后的零
给定一个整数 n,返回 n! 结果尾数中零的数量。
输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。
解题思路
参考大佬的思路:
首先题目的意思是末尾有几个0
比如6! = 【1 × 2 × 3 × 4 × 5 × 6】
其中只有2×5末尾才有0,所以就可以抛去其他数据 专门看2、5 以及其倍数,毕竟 4 × 25末尾也是0
比如10! = 【2 × 4 × 5 × 6 × 8 × 10】
其中 4能拆成2×2 、10能拆成2×5
所以10! = 【2 ×(2 ×2)× 5 ×(2 × 3)×(2 × 2 × 2) ×(2 × 5)】
一个2和一个5配对 就产生一个0,所以10!末尾2个0
转头一想 2肯定比5多 所以只数5的个数就行了
对于一个数的阶乘,就如之前分析的,5 的因子一定是每隔 5 个数出现一次,也就是下边的样子。
n ! = 1 ∗ 2 ∗ 3 ∗ 4 ∗ ( 1 ∗ 5 ) ∗ . . . ∗ ( 2 ∗ 5 ) ∗ . . . ∗ ( 3 ∗ 5 ) ∗ . . . ∗ n n! = 1 * 2 * 3 * 4 * (1 * 5) * ... * (2 * 5) * ... * (3 * 5) *... * n n!=1∗2∗3∗4∗(1∗5)∗...∗(2∗5)∗...∗(3∗5)∗...∗n
因为每隔 5 个数出现一个 5,所以计算出现了多少个 5,我们只需要用 n/5 就可以算出来。
. . . ∗ ( 1 ∗ 5 ) ∗ . . . ∗ ( 1 ∗ 5 ∗ 5 ) ∗ . . . ∗ ( 2 ∗ 5 ∗ 5 ) ∗ . . . ∗ ( 3 ∗ 5 ∗ 5 ) ∗ . . . ∗ n ... * (1 * 5) * ... * (1 * 5 * 5) * ... * (2 * 5 * 5) * ... * (3 * 5 * 5) * ... * n ...∗(1∗5)∗...∗(1∗5∗5)∗...∗(2∗5∗5)∗...∗(3∗5∗5)∗...∗n
每隔 25 个数字,出现的是两个 5,所以除了每隔 5 个数算作一个 5,每隔 25 个数,还需要多算一个 5。
也就是我们需要再加上 n / 25 个 5。
同理我们还会发现每隔 5 ∗ 5 ∗ 5 = 125 5 * 5 * 5 = 125 5∗5∗5=125 个数字,会出现 3 个 5,所以我们还需要再加上 n / 125 。
综上,规律就是每隔 5 个数,出现一个 5,每隔 25 个数,出现 2 个 5,每隔 125 个数,出现 3 个 5… 以此类推。
最终 5 的个数就是 n / 5 + n / 25 + n / 125... n / 5 + n / 25 + n / 125 ... n/5+n/25+n/125...
写程序的话,如果直接按照上边的式子计算,分母可能会造成溢出。所以算 n / 25 的时候,我们先把 n 更新,n = n / 5,然后再计算 n / 5 即可。后边的同理。
int trailingZeroes(int n){
int count = 0;
while(n > 0){
count += n / 5; //统计当前n的 因数5的个数
n /= 5; //更新n,进行下一次循环统计
}
return count;
}
或递归形式
int trailingZeroes(int n){
return n == 0 ? 0 : (n / 5 + trailingZeroes(n / 5));
}
204. 计数质数
统计所有小于非负整数 n 的质数的数量。
思路: Eratosthenes 筛选法
图片来自 Wikipedia - Sieve_of_Eratosthenes_animation.gif
int countPrimes(int n){
if(n == 0 || n == 1) //0和1都不是质数
return 0;
int *flag = malloc(sizeof(int) * n);
memset(flag, 0 ,sizeof(int) * n);
long int i, j, count = 0; //int溢出,用long int
for(i = 2; i < n; i++){
if(flag[i]) //合数,flag[i] != 0
continue;
count++; //遇到质数进行统计
flag[i] = 1;
for(j = i * i; j < n; j += i){ //i的倍数全为合数
flag[j] = 1;
}
}
free(flag);
return count;
}
最大公因数 & 最小公倍数
求最小公倍数算法:
最小公倍数 = 两整数的乘积 ÷ 最大公约数
求最大公约数算法: 辗转相除法
有两整数a和b:
① a%b得余数c
② 若c=0,则b即为两数的最大公约数
③ 若c≠0,则a=b,b=c,再回去执行①
例如求27和15的最大公约数过程为:
27÷15 余1215÷12余312÷3余0因此,3即为最大公约数
//求a,b的最大公因数
int GCD(int a, int b) {
int temp;
do {
temp = a % b;
if(temp == 0)
return b;
else {
a = b;
b = temp;
}
} while(temp != 0);
}
//求m和n的最小公倍数
int LCM(int m, int n) {
int x, y, c;
x = m; y = n;
while(m != 0) {
c = n % m;
n = m;
m = c;
}
return (x * y / n);
}
367. 有效的完全平方数
给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。
说明:不要使用任何内置的库函数,如sqrt
。
输入:16
输出:True
解题思路
完全平方数可以通过累加从1往后的奇数找到,
1 = 1;
4 = 1 + 3;
9 = 1 + 3 + 5;
16 = 1 + 3 + 5 + 7;
…
bool isPerfectSquare(int num){
if(num == 0)
return false;
int i = 1;
while(num > 0){
num -= i; //通过一步步减去递增奇数后是否为零来判断
i += 2;
}
return num == 0 ? true : false;
}
LCP 06. 拿硬币
桌上有 n 堆力扣币,每堆的数量保存在数组 coins 中。我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数。
示例 1 :
输入: [4,2,1]
输出: 4
解释: 第一堆力扣币最少需要拿 2 次,第二堆最少需要拿 1 次,第三堆最少需要拿 1 次,总共 4 次即可拿完。
示例 2:
输入: [2,3,10]
输出: 8
限制:
1 <= n <= 4
1 <= coins[i] <= 10
int minCount(int* coins, int coinsSize){
int res = 0;
for(int i = 0; i < coinsSize; i++){
//一堆硬币最多拿两个,因此一堆2个2个拿最多拿 coins[i] / 2 次
//拿完两个还剩 coins[i] % 2 个,即还需拿1次或0次
res += coins[i] / 2 + coins[i] % 2;
}
return res;
}