最近做的一道acwing算法题:题在这里
这里总结以下里面学到的新的知识点:
1、1既不是质数也不是合数
2、关于最大公约数:gcd(a,b) = gcd(b,a%b)(a%b!=0)具体的证明可以看百科
3、关于质因数分解之前有写过:这里会给出代码
#include <iostream> #include <vector> #include <unordered_map> using namespace std; template<typename T> std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec) { for (auto& el : vec) { os << el << ' '; } return os; } //统计数字num的质因数有哪些 template<typename T> void primeFac(vector<T> &pf,T num) { for(T i = 2;i*i<=num;i++) { if(num%i==0) { pf.push_back(i); while(num%i==0) num/=i; } } if(num!=1) pf.push_back(num); } //统计数字num的质因数及其出现的次数 template<typename T> void primeFac(unordered_map<T,T> &pf,T num) { for(T i = 2;i*i<=num;i++) { while(num%i==0) { num/=i; pf[i]++; } } if(num!=1) pf[num]++; } int main() { vector<int> k; unordered_map<int,int> pf; primeFac(k,120); cout<<k<<endl; primeFac(pf,120); for(auto it=pf.begin();it!=pf.end();it++) cout<<it->first<<" "<<it->second<<endl; return 0; }
4、关于欧拉函数:关于具体的内容可以开百科,这里只写结论:
上面的φ(m)是欧拉函数,表示的是在数据范围[1,m]中与m互质的数的个数。其中p是质数,这里就可以认为是m的质因数
5、简化剩余系(缩系、既约剩余系)
这里讲一下这个是因为感觉百科里面没讲得太清楚,这里只讨论正数:
首先数A%m可以产生的结果集合是【0,1,2,3,.....,m-1】,总共m个数,其中与m互质的数设为
【
】这就是众多简化剩余系其中的一个。
的A构成一个集合
(同余类),那么【
】就可以有n个集合【
】,从每一个集合里面挑出来一个数构成的集合就是一个简化剩余系,挑出来的每一个数都与m互质。(理解有错的的话请指正)
STL中的三个判断函数:all_of,none_of,any_of
vector<int> arr{3,2,4,5,6}; //这句话表示在arr中所有的值都要满足return 后面的表达式才会返回true all_of(arr.begin(),arr.end(),[](int &a){return a>=2;}); //这句话表示在arr中有任何一个值满足return 后面的表达式就会返回true any_of(arr.begin(),arr.end(),[](int &a){return a>=2;}); //这句话表示在arr中没有任何值满足return 后面的表达式才会返回true none_of(arr.begin(),arr.end(),[](int &a){return a>=2;});
accumulate
template< class InputIt, class T > constexpr T accumulate( InputIt first, InputIt last, T init ); template< class InputIt, class T, class BinaryOperation > constexpr T accumulate( InputIt first, InputIt last, T init, BinaryOperation op );
std::multiplies:乘法。
std::minus:减法。
std::plus:加法,默认。
std::divides:除法
std::modulus:模运算。
原文链接
对于一个大于1正整数n可以分解质因数:
则n的正约数的个数就是
其中a1、a2、a3…ak是p1、p2、p3,…pk的指数。
其中p1,p2,p3...pk是质数。f(n)包括了数字1和n本身。
例如:12 = 2^2 * 3
f(12) = (2+1)*(1+1)=6个正约数(1,2,3,4,6,12)
具体可以可以看约数定理个数
字典树的结构,以后会慢慢完善这个结构,使其能够更加模板化
struct TrieNode{ vector<TrieNode *> child; bool isEnd;//可以方便的判断节点是否是尾部,如果没有isEnd就得判断child是否都为空 int cnt;//cnt的定义可以自己定义,可以比较方便的计数 TrieNode() { this->child = vector<TrieNode *>(26,nullptr); this->isEnd = false; this->cnt = 0; } }; class TrieTree{ private: TrieNode *root; public: TrieTree() { root = new TrieNode(); } //val的定义可以自己定义,比如添加(val = 1)或者删除(val = -1) void insert(string &str,int val) { TrieNode *t = root; for(char &s:str) { if(t->child[s-'a']==nullptr) t->child[s-'a'] = new TrieNode(); t = t->child[s-'a']; t->cnt+=val; } t->isEnd = true; } int search(string &str) { TrieNode *t = root; for(char &s:str) { if(t->child[s-'a']==nullptr) return 0; else t = t->child[s-'a']; } return t->cnt; } };
大写字母和小写字母的差别:
大写字母的二进制在1<<5的位置上是0
小写字母的二进制在1<<5的位置上是1
字符串由字母构成:string word
string word = "aSaDGAFaSDFAsdfAsdFasD"; for(char &c:word) { if(c&32) cout<<"小写字母"<<endl; else cout<<"大写字母"<<endl; }
顺便添加一个大小写字母的转换:
字母大小写转换可以使用位运算: 大写变小写、小写变大写 : 字符 ch^= 32; 大写变小写、小写变小写 : 字符 ch|= 32; 小写变大写、大写变大写 : 字符 ch&= -33;
//-2147483648==INT_MIN==(1000 0000 0000 0000 0000 0000 0000 0000)2==-pow(2,31)
abs(int x):
int负数最小值-2147483648在使用abs后没有变,还是-2147483648
int m = -2147483648;
m = abs(m);
m的值依旧是-2147483648
这里是范围溢出
今天写算法的时候遇到的一个比较实用的算法,这里记录一下以后可能会用到。。
数组nums是一个有序的升序数组,在数组里面寻找target的边界下边
示例:nums = 【1,2,3,4,4,4,5,7,7,8,9】 , target = 4
lower = true表示寻找下边界,寻找数组里面的第一个target的下标
lower = false表示寻找上边界,寻找数组里面的最后一个target的下标
如果数组里面没有target就返回-1
template<typename T> int binarySearch(vector<T> &nums,T target,bool lower) { int mid, l = 0, r = nums.size() - 1; int ans = -1; while(l<=r) { mid = (r-l)/2+l; if(nums[mid]>target) r = mid - 1; else if(nums[mid]<target) l = mid + 1; else { ans = mid; lower?r = mid - 1:l = mid + 1; } } return ans; }
今天做leetcode的时候遇到一个问题:
10e9+7和10^9+7(^表示次方)不相等
10e9+7=10000000007
10^9+7=1000000007
后来查了一下,其实10e9是科学计数法的表示方式,这个在上学的时候肯定学过,但是用得少所以就忘记怎么算的了。
这里10e9表示的是10*10^9(^表示次方),即10*(10的9次方)
C++输出指定精度的浮点数
double PI= 3.1415926535898 cout<<setiosflags(ios::fixed)<<setprecision(6)<<PI;//C++输出指定精度浮点数 //printf("%.6f",PI);//c输出指定进度浮点数
补码加法成为:[X+Y]补 = [X]补 + [Y]补
补码的减法变为:[X-Y]补 = [X]补 - [Y]补 = [X]补 + [-Y]补
补码的乘法变为:【X*Y】补=【X】补×【Y】补;
1、正数的补码就是其本身,负数的补码是符号位不变,其余位取反,然后加1
2、计算机中数值一律使用补码来表示和存储
原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理
3、a<0,b<0 补码((补码a)+(补码b))=a+b 这个加就是直接加,不用管溢出
重载vector<T>输出
template<typename T> std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec) { for (auto& el : vec) { os << el << ' '; } return os; }
一个int整数,如果要移除其二进制表示的最低位1可以:n = n&(n-1)
如果要单独取出其二进制表示的最低位1可以:n = n&(-n)
https://leetcode-cn.com/problems/power-of-two/solution/2de-mi-by-leetcode-solution-rny3/
优先级队列自定义比较函数
//--------------------------------------------- typedef struct node{ int time; int p; }Node; struct cmp { bool operator()(Node &a,Node &b) { if(a.time==b.time) return nodestr[a.p]<nodestr[b.p]; return a.time>b.time; } }; priority_queue<Node,vector<Node>,cmp> prique; //--------------------------------------------------- #include <iostream> #include <queue> #include <vector> using namespace std; vector<vector<int>> edge{ {0,2,10}, {0,4,30}, {0,5,100}, {1,2,5}, {2,3,50}, {3,5,10}, {4,5,60}, {4,3,20} }; struct cmp{ bool operator()(int i,int j) { return edge[i][2]<edge[j][2]; } }; int main() { //传入的int值,cmp实际比较的不是int的大小,而是比较其他值的大小 priority_queue<int,vector<int>,cmp> prique; return 0; } //--------------------------------------------------------
求第k大的数,可以用nth_element,也可以用堆算法
std::nth_element
应用的范围由它的第一个和第三个参数指定。第二个参数是一个指向第 n 个元素的迭代器。如果这个范围内的元素是完全有序的,nth_dement() 的执行会导致第 n 个元素被放置在适当的位置。这个范围内,在第 n 个元素之前的元素都小于,等于第 n 个元素,而且它后面的每个元素都会比它大。算法默认用 < 运算符来生成这个结果。
std::vector<int> numbers {22, 7, 93, 45, 19, 56, 88, 12, 8, 7, 15, 10}; size_t count {5}; // Index of nth element std::nth_element(std::begin(numbers), std::begin(numbers) + n, std::end(numbers));
简单的说:就是求numbers中的第n+1大的数
第 n 个元素之前的元素都小于它,但不必是有序的。同样,第 n 个元素后的元素都大于它,但也不必是有序的。如果第二个参数和第三个参数相同(元素段的末尾),这时这个算法是无效的。
这里重新整理了一下几个与二分有关函数:
lower_bound(begin,end,val)
lower_bound(begin,end,val,cmp)
返回一个迭代器,迭代器指向第一个不小于val的数
如果所有的数都比val小,则返回末尾的endupper_bound(begin,end,val)
upper_bound(begin,end,val,cmp)
返回一个迭代器,迭代器指向第一个大于val的数
如果所有的数都小于等于val,则返回末尾的endbinary_serach(begin,end,val)
binary_serach(begin,end,val,cmp)
返回一个布尔值
返回true:表示在范围[begin,end)中存在值为val的数
返回false:表示在范围[begin,end)中不存在值为val的数equal_range(begin,end,val)
equal_range(begin,end,val,cmp)
返回一个pair<iterator start,iterator end>指向一个范围
在范围[start,end)中的所有数都等于valfirst 成员的值等同于 std::lower_bound 执行于同一输入序列后的返回。
second 成员的值等同于 std::upper_bound 执行于同一输入序列后的返回。#include <iostream> #include <algorithm> #include <vector> using namespace std; int main() { vector<int> a{1,3,5,7,9}; cout<<*lower_bound(a.begin(),a.end(),3)<<endl;//3 cout<<*lower_bound(a.begin(),a.end(),6)<<endl;//7 cout<<*lower_bound(a.begin(),a.end(),10)<<endl;//0 cout<<*upper_bound(a.begin(),a.end(),3)<<endl;//5 cout<<*upper_bound(a.begin(),a.end(),6)<<endl;//7 cout<<*upper_bound(a.begin(),a.end(),9)<<endl;//0 cout<<*upper_bound(a.begin(),a.end(),10)<<endl;//0 return 0; }
这个我自己理解了一下cmp这个比较函数:
比如容器:vector<int> arr{1,3,5,7,9};
lower_bound(arr.begin(),arr.end(),val,cmp)在lower_bound中cmp默认的是operator<运算,也就是升序排列,lower_bound本身要求容器arr的排序方式和自己的排序方式(cmp)一样。
#include <iostream> #include <algorithm> #include <vector> using namespace std; bool cmp1(int i,int j) { return i<j; } bool cmp2(int i,int j) { return i>j; } int main() { vector<int> a{1,5,4,2,3,6,9,8}; sort(a.begin(),a.end(),cmp1);//1,2,3,4,5,6,8,9 cout<<*lower_bound(a.begin(),a.end(),3,cmp1)<<endl;//3 //这个是找到第一个不小于3的数 sort(a.begin(),a.end(),cmp2);//9,8,6,5,4,3,2,1 cout<<*lower_bound(a.begin(),a.end(),6,cmp2)<<endl;//6 //这个是找到第一个不大于6的数 }