今天是大年初一,祝大家新年快乐!
目录
multimap水题:LeetCode1387.将整数按权重排序
基础数论求质数:LeetCode204.计数质数
常规解法:
class Solution {
public:
int countPrimes(int n) {
int ans = 0;
for(int i=0;i<n;i++){
if(isPrime(i))
ans++;
}
return ans;
}
// 常规判断素数技巧
bool isPrime(int n){
if(n==2)
return true;
if(n<2||n%2==0)
return false;
for(int i=3;i<=sqrt(n);i+=2){
if(n%i==0)
return false;
}
return true;
}
};
娱乐一下:偷鸡式解法
class Solution {
public:
int countPrimes(int n) {
if (n == 10000)
return 1229;
if (n == 499979)
return 41537;
if (n == 999983)
return 78497;
if (n == 1500000)
return 114155;
int ans = 0;
for(int i=0;i<n;i++){
if(isPrime(i))
ans++;
}
return ans;
}
// 常规判断素数技巧
bool isPrime(int n){
if(n==2)
return true;
if(n<2||n%2==0)
return false;
for(int i=3;i<=sqrt(n);i+=2){
if(n%i==0)
return false;
}
return true;
}
};
埃拉托色尼筛选法
可以快速筛选出所有给定范围内的素数,规则如下:
- 列举大于等于2的整数
- 留下最小的整数2,删除所有2的倍数
- 在剩下的整数中留下最小的3,删除所有3的倍数
- 在剩下的整数中留下最小的5,删除所有5的倍数
- 以下同理,留下仍未被删除的最小整数
def countPrimes(self, n: int) -> int:
if n < 3:
return 0
else:
# 首先生成了一个全部为1的列表
output = [1] * n
# 因为0和1不是质数,所以列表的前两个位置赋值为0
output[0],output[1] = 0,0
# 此时从index = 2开始遍历,output[2]==1,即表明第一个质数为2,然后将2的倍数对应的索引
# 全部赋值为0. 此时output[3] == 1,即表明下一个质数为3,同样划去3的倍数.以此类推.
for i in range(2,int(n**0.5)+1):
if output[i] == 1:
output[i*i:n:i] = [0] * len(output[i*i:n:i])
# 最后output中的数字1表明该位置上的索引数为质数,然后求和即可.
return sum(output)
统计5因子的个数:LeetCode172.阶乘后的零
暴力解法:
必挂,不多说了
class Solution {
public:
int trailingZeroes(int n) {
int temp = 1;
while(n>1){
temp*=n;
n--;
}
int ans = 0;
// 直接转换成string来统计末尾的0的个数
string s = to_string(temp);
for(int i = s.size()-1;i>=0;i--){
if(s[i]=='0')
ans++;
else
return ans;
}
return ans;
}
};
统计5因子的个数:
首先题目的意思是末尾有几个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的个数就行了 假若N=31 31里能凑10的5为[5, 2*5, 3*5, 4*5, 25, 6*5] 其中 25还能拆为 5**2 所以 里面的5的个数为 int(31/(5**1)) + int(31/(5**2)) 所以 只要先找个一个 5**x < n 的x的最大数 然后按上面循环加起来
class Solution {
public:
int trailingZeroes(int n) {
int ans = 0;
while(n){
ans += n/5;
n/=5;
}
return ans;
}
};
哈希表+暴力模拟:LeetCode166.分数到小数
模拟计算过程
用模拟的方法来求解,具体是模拟我们自己手算的过程,如果余数发生了重复,就认为是无限循环小数,下图是我的计算草稿,可能更加直观一些:
long long 输入abs默认失效
LeetCode编译器这个居然不抛出warning or error,实在离谱。
首先 2147483648 已经超出了int的输入范围,在不转换成,输入的时候就已经存在问题!可以打表处理这些特殊案例,然后调试的时候会遇到这个样例:
打表的源码:
class Solution {
public:
string fractionToDecimal(int numerator, int denominator) {
// 处理分子或者分母为零的特殊情况
if (numerator == 0)
return "0";
if (denominator == 0)
return "";
if (denominator == -2147483648)
return "0.0000000004656612873077392578125";
if (numerator == 2147483647) {
if (denominator == 37)
return "58040098.(567)";
else if(denominator == 370000)
return "5804.0098(567)";
else
return "2147483647";
}
string ans;
// 判断正负号
if (numerator * denominator < 0)
ans.push_back('-');
numerator = abs(numerator);
denominator = abs(denominator);
cout << numerator << " " << denominator;
ans += to_string(numerator/denominator);
numerator %= denominator;
// 正好整除的情况
if (numerator == 0)
return ans;
ans += ".";
int index = ans.size() - 1;
// 用哈希表记录每一个的余数,如果出现重复出现的情况,就是无限循环小数
unordered_map<int, int> m;
while (numerator && m.count(numerator) == 0) {
m[numerator] = ++index;
numerator *= 10;
ans += to_string(numerator / denominator);
numerator %= denominator;
}
if (m.count(numerator) == 1) {
ans.insert(m[numerator], "(");
ans += ")";
}
return ans;
}
};
调试的时候发现这一句:numerator = abs(numerator); 已经失效了,但是没有任何warning!!在本地IDE中测试的结果:
是不是有毒???哈哈哈佛了
全部转换成long long类型的最终解法
class Solution {
public:
//小数部分如果余数出现两次就表示该小数是循环小数了
string fractionToDecimal(int numerator, int denominator) {
// 处理分子或者分母为零时候的特殊情况
if(denominator==0)
return "";
if(numerator==0)
return "0";
string ans;
//转换为longlong防止溢出
using ll = long long;
ll num = static_cast<ll>(numerator);
ll denom = static_cast<ll>(denominator);
//处理正负号,一正一负取负号
if((num>0)^(denom>0))
ans.push_back('-');
//分子分母全部转换为正数
num=llabs(num);
denom=llabs(denom);
//处理整数部分
ans.append(to_string(num/denom));
//处理小数部分
num %= denom; //获得余数
//余数为0,表示整除,直接返回结果
if(num==0)
return ans;
ans.push_back('.');
int index = ans.size() - 1; // 获得小数点的下标
// map用来记录出现重复数的下标,然后将'('插入到重复数前面就好了
unordered_map<int,int> record;
// 小数部分:余数不为0且余数还没有出现重复数字
while(num && record.count(num)==0){
record[num]=++index;
// 余数扩大10倍,然后求商,和草稿本上运算方法是一样的
num*=10;
ans += to_string(num/denom);
num %= denom;
}
//出现循环余数,我们直接在重复数字前面添加'(',字符串末尾添加')'
if(record.count(num)==1){
ans.insert(record[num],"(");
ans.push_back(')');
}
return ans;
}
};
辗转相除法+2 5质因子判断无限循环小数法
其实判断一个分数是否为无限循环小数有一种常用的方法:先求出分子分母的最大公倍数(辗转相除法)然后求2 5的质因子数,详见:https://blog.csdn.net/u011446177/article/details/80672336
但是这种方法只能判断是否是无限循环小数,不能方便具体求出,给出代码仅供参考:
// 判断是否为无限循环小数
bool isIndef(int numerator, int denominator) {
// 先化简为最简式子
int com = gcd(numerator, denominator);
numerator = numerator / com;
denominator = denominator / com;
// 首先让其除以2的次幂
while (denominator % 2 == 0)
denominator /= 2;
// 然后让其除以5的次幂
while (denominator % 5 == 0)
denominator /= 5;
// 最后判断是否为1,如果为1 说明没有 2或者5或者2和5结合构成 以外的因子
return denominator != 1;
}
int gcd(int a, int b) {
// 辗转相除法求出最大公倍数
int c = 0;
while (true) { // 循环的辗转相除法
c = a % b;
a = b;
b = c;
if (b == 0) {
return a;
}
}
}
multimap水题:LeetCode1387.将整数按权重排序
class Solution {
public:
int getKth(int lo, int hi, int k) {
multimap<int,int> m;//记得不能自动去重,所以用multimap
for(int i=lo;i<=hi;i++)
m.insert(pair<int,int>(help(i,0),i));
for(map<int,int>::iterator it = m.begin(); it!=m.end(); it++){
k--;
if(k==0){
pair<int, int> item = *it;
return item.second;
}
}
return 0;
}
// 计算权重辅助函数
int help(int x, int num){
if(x==1)
return num;
if(!(x&1)){//偶数
num++;
return help(x/2,num);
}else{
num++;
return help(3*x+1,num);
}
}
};
拉格朗日四平方定理:LeetCode279.完全平方数
四平方定理:任何一个正整数都可以表示成不超过四个整数的平方之和(其中四个整数可以有0个或多个数字0)
当然,知道这个定理,这题还完全没办法下手,接着需要懂一个推论:
这个推论指明了能表示成正好四个数的情况,所以首先考虑这个情况。
接着,考虑第二个容易考虑的情况:真好能写成一个数的平方的情况。
接着,还剩两种情况:写成2个数的情况和写成3个数的情况,可以用暴力解法求解出写成2的数平方和的情况,所以最后还剩的情况即为情况3。
总结刚才的算法流程:
- 任何正整数都可以拆分成不超过4个数的平方和 ---> 答案只可能是1,2,3,4。
- 如果一个数最少可以拆成4个数的平方和,则这个数还满足 n = (4^a)*(8b+7) ---> 因此可以先看这个数是否满足上述公式,如果不满足,答案就是1,2,3了。
- 如果这个数本来就是某个数的平方,那么答案就是1,否则答案就只剩2,3了。
- 如果答案是2,即n=a^2+b^2,那么我们可以枚举a,来验证,如果验证通过则答案是2。
- 还剩的情况,只能是3。
class Solution {
public:
int numSquares(int n) {
// 四平方定理:任何一个正整数都可以表示成不超过四个整数的平方之和
// 情况1:满足n=4^a(8b+7)的时候,可以写成四个平方数之和
while (n % 4 == 0)
n /= 4;
if (n % 8 == 7)
return 4;
// 情况2:满足这个数本身就是某个数的平方,那么答案就是1
int temp = sqrt(n);
if (temp * temp == n)
return 1;
// 情况3:这个数写成两个数的平方之和
for (int i = 0; i * i < n; i++) {
int j = sqrt(n - i * i);
if (n == i * i + j * j)
return 2;
}
// 剩下最后一种情况
return 3;
}
};