刷算法题的一些小笔记

 最近做的一道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、关于欧拉函数:关于具体的内容可以开百科,这里只写结论:

\varphi(m)=m\prod_{p|m}(1-\frac{1}{p})

上面的φ(m)是欧拉函数,表示的是在数据范围[1,m]中与m互质的数的个数。其中p是质数,这里就可以认为是m的质因数

5、简化剩余系(缩系、既约剩余系)

这里讲一下这个是因为感觉百科里面没讲得太清楚,这里只讨论正数:

首先数A%m可以产生的结果集合是【0,1,2,3,.....,m-1】,总共m个数,其中与m互质的数设为

x_{1},x_{2},x_{3},....,x_{n}】这就是众多简化剩余系其中的一个。

A%m==x_{i}的A构成一个集合A_{i}(同余类),那么【x_{1},x_{2},x_{3},....,x_{n}】就可以有n个集合【A_{1},A_{2},A_{3},...,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=\prod_{i=1}^{k}p_{i}^{a_{i}}=p_{1}^{a_{1}}\cdot p_{2}^{a_{2}}\cdot \cdot \cdot p_{k}^{a_{k}}

则n的正约数的个数就是f(n)=\coprod_{i=1}^{k}(a_{i}+1)=(a_{1}+1)\cdot (a_{2}+1)\cdot (a_{3}+1)\cdot \cdot \cdot (a_{k}+1)

其中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小,则返回末尾的end

upper_bound(begin,end,val)
upper_bound(begin,end,val,cmp)
返回一个迭代器,迭代器指向第一个大于val的数
如果所有的数都小于等于val,则返回末尾的end

binary_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)中的所有数都等于val

first 成员的值等同于 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的数
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值