最全算法模板-更新ing


1.快速排序-4种

	void quick_sort(int a[],int left,int right)
	{
	    if(left>=right) return;
	    //四种写法,如果选用不合适的方法配对的话会导致无限划分
	    //-1边界left while循环可求l范围(left,right) r范围(left,right-1) 不能用l分界,任选下2
	    int l=left-1,r=right+1,x=a[(left+right)/2];
	    int l=left-1,r=right+1,x=a[left];
	    //-2边界right 通过while循环可求l范围(left+1,right)[猜] r范围(left,right)不能用r分界,任选下2
	    int l=left-1,r=right+1,x=a[(left+right+1)/2];
	    int l=left-1,r=right+1,x=a[right];
	    while(l<r)
	    {
	        do l++; while(a[l]<x);
	        do r--; while(a[r]>x);
	        if(l<=r) swap(a[l],a[r]);
	    }
	    //两种写法都满足将一个整体切成小于x和大于x两块
	    quick_sort(a,left,r);quick_sort(a,r+1,right);//-1 r分界
	    quick_sort(a,left,l-1);quick_sort(a,l,right);//-2 l分界
	}

2.归并排序

	void merge_sort(int q[],int l,int r)
	{
	    if(l>=r) return;
	    int mid=l+(r-l)/2;
	    merge_sort(q,l,mid);
	    merge_sort(q,mid+1,r);
	    int temp[r-l+1],k=0,n=0,left=l,j=l,right=mid+1;
	    while(left<=mid&&right<=r)
	    {
	        if(q[left]<q[right]) temp[k++]=q[left++];
	        else temp[k++]=q[right++];
	    }
	    while(left<=mid) temp[k++]=q[left++];
	    while(right<=r) temp[k++]=q[right++];
	    while(n<k) q[j++]=temp[n++];
	}

3.二分法-整数-2种

	int l=0,r=n-1,mid;
	while(l<r)
	{
	    mid=(l+r)/2;
	    //获得满足条件最小值
	    if(check(mid)) r=mid;
	    else l=mid+1;
	}while(l<r)
	{
	    mid=(l+r+1)/2;//不+1可能进入死循环
	    //获得满足条件最大值
	    if(check(mid)) l=mid;
	    else r=mid-1;
	}
	//退出时l,r相等 输出任意一个皆可
	//if(check(l)) return l;查找成功
	//return -1;查找失败

3.二分法-小数-2种

	double l=xxx,r=xxx,mid;//根据题意选择合适端点
	while(r-l>=1e-8)//最好比题目要求精度多两位6+2=8
	{
	   mid=(l+r)/2;
	   //获得满足条件最小值
	   if (check(mid)) r=mid;
	   else l=mid;//不用+1
	}while(r-l>=1e-8)//最好比题目要求精度多两位6+2=8
	{
	   mid=(l+r)/2;//不用+1,因为是浮点数,几乎很难出现死循环的情况
	   //获得满足条件最大值
	   if (check(mid)) l=mid;
	   else r=mid;//不用-1
	}
	//退出时l,r,mid近似相等,输出任意一个皆可
	//if(check(mid)) return l;查找成功
	//return -1;查找失败

4.高精度计算-加法-不压位

	int t=0;//在此之前需要处理使得左边数据大于右边
	for(int i=0;i<a.size();i++)
	{
	    t+=a[i];
	    if(i<b.size()) t+=b[i];
	    c.push_back(t%10);//注意不要写反%和/
	    t=t/10;
	}
	if(t) c.push_back(1);

4.高精度计算-加法-压位-待补充

	************

4.高精度计算-减法

	int t=0;//在此之前需要处理使得左边数据大于右边
	for(int i=0;i<a.size();i++)
	{
	    t=a[i]-t;
	    if(i<b.size()) t-=b[i];
	    c.push_back((t+10)%10);
	    if(t<0) t=1;//是否借位
	    else t=0;
	}
	while(c.size()>1&&c.back()==0) c.pop_back();//删除前导0

4.高精度计算-乘法

	int t=0;//被乘数a,乘数b
	for(int i=0;i<a.size()||t;i++)
	{
	    if(i<a.size()) t+=a[i]*b;//跟加法非常类似
	    c.push_back(t%10);
	    t=t/10;
	}
	while(c.size()>1&&c.back()==0) c.pop_back();//删除前导0

4.高精度计算-除法

	int t=0;//被除数a,除数b
	for(int i=a.size()-1;i>=0;i--)//与加减乘相反,从高位开始处理
	{
	    t=t*10+a[i];
	    c.push_back(t/b);
	    t%= b;
	}
	reverse(c.begin(),c.end());//没有pop_front,必须逆序后处理前导0
	while(c.size()>1&&c.back()==0) c.pop_back();

5.前缀和<—逆向—>差分-一维

	void insert(int l,int r,int c)//对差分进行处理的函数
	{
	   b[l]+=c;
	   b[r+1]-=c;
	}
	insert(i,i,x);//b[i]存入的就是此时的差分
	insert(l1,l2);//对某一段差分进行处理
	b[i]+=b[i-1];//前缀和计算原本的数据

5.前缀和<—逆向—>差分-二维

	void insert(int l1,int r1,int l2,int r2,int c)//对差分进行处理的函数
	{
	    b[l1][r1]+=c;
	    b[l1][r2+1]-=c;
	    b[l2+1][r1]-=c;
	    b[l2+1][r2+1]+=c;
	}
	insert(i,j,i,j,x);//b[i][j]存入的就是此时的差分
	insert(l1,r1,l2,r2,x);//对某一段差分进行处理
	b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];//前缀和计算原本的数据

6.双指针

根据条件设置

7.位运算

n>>k&1//n的第k位数字
n&(n-1)//n的最后一位为1的数字

8.离散化

	typedef pair<int,int> PII;
	vector<PII> add;
	vector<int> all;//实际需要操作的坐标点
	int a[xxx];
	int find(int x)//二分查找映射到all的坐标
	sort(all.begin(), all.end());
	all.erase(unique(all.begin(), all.end()), all.end());
	//在映射后数组处理,如此处加
	for(auto item:add)
	{
	    int x = find(item.first);
	    a[x] += item.second;
	}

9.区间合并

	typedef pair<int,int> PII;
	void merge(vector<PII> &segs)
	{
	    vector<PII> res;
	    sort(segs.begin(),segs.end());//先以first再以second进行sort
	    int st=-2e9,ed=-2e9;//都选择最左边的部分
	    for(auto seg:segs)
		    if(ed<seg.first)//无交叉
		    {
		        if(st!=-2e9) res.push_back({st,ed});//第一个可能后续还有能合并的区间不能直接插入
		        st=seg.first,ed=seg.second;
		    }
	        else ed=max(ed,seg.second);//有交叉
	    if (st!=-2e9) res.push_back({st,ed});//最后一个区间可能还因为有交叉没有加入
	    segs=res;
	}

10.单链表(数组模拟)

	void init()//初始化
	{
	    head=-1;//开始只有末尾的空节点,不像双指针有左右不使用节点
	    idex=0;//将要插入的节点的数组下标
	}
	void head_insert(int x)//每一个新节点都在头
	{
	    e[idex]=x;
	    ne[idex]=head;
	    head=idex++;
	}
	void del(int k)//删除插入的第k个节点,因为idex此处为0,调用时需要-1
	{
	    ne[k]=ne[ne[k]];
	}
	void insert(int k,int x)//插入在第k个节点后,因为idex此处为0,调用时需要-1
	{
	    e[idex]=x;
	    ne[idex]=ne[k];
	    ne[k]=idex++;
	}
	for(int i=head;i!=-1;i=ne[i]) printf("%d ",e[i]);//并不受下标顺序影响

10.双链表(数组模拟)

	void insert(int a, int x)
	{
	    e[idx]=x;
	    l[idx]=a,r[idx]=r[a];
	    l[r[a]]=idx,r[a]=idx++;
	}
	// 删除节点a
	void remove(int a)
	{
	    l[r[a]]=l[a];
	    r[l[a]]=r[a];
	}

11.单调栈(数组模拟)

	-----------------------------------对于表达式计算----------------------------------------
	1.数字时直接插入数字栈
	2.左括号时直接插入符号栈
	3.右括号时数字和字符从栈中弹出进行计算后在放入数字栈[最后的左括号也需要出栈]
	4.其他符号若比栈中的优先级小,数字栈弹出数字处理,直到无字符或者出现左括号停止,否则入栈
	5.表达式遍历完后要查看符号栈是否还有没有使用的符号,也需要进行处理
	---------------------------------------------------------------------------------------
	//左边最近比它小 左边最近比它大a[index]<=x 右边的两种情况逆序两次
	while(index&&a[index]>=x) index--;//a数组表示已经插入,x表示准备插入
	if(!index) printf("-1 ");
	else printf("%d ",a[index]);
	a[++index]=x;//原来减多了需要补回来

12.单调队列(数组模拟)

	int h[N],a[N];//存储下标和存储数值
	for(int i=0;i<n;i++)
	{
	    //以下两个while都在元素非空的情况下
	    //保持宽度为k
	    while(front<=rear&&k<i-h[front]+1) front++;
	    //保持元素递增,此时的元素必进队列,队列里不满足条件的出队
	    while(front<=rear&&a[h[rear]]>=a[i]) rear--;
	    h[++rear]=i;
	    //从滑动窗口宽度够的时候开始就要输出值,由于递增最左边是最小值
	    if(i+1>=k) printf("%d ",a[h[front]]);
	}
	//保持元素递增获得的滑动窗口最右[最大值]和保持元素递减获得的滑动窗口最左[最大值]并不相同

13.KMP

	//p为模式串,n为长度 s为匹配串,m为长度
	for(int j=0,i=1;i<n;i++)
	{
	    while(j&&p[i]!=p[j]) j=ne[j-1];//回到相同0~j-1跟此时
	    if(p[i]==p[j]) j++;
	    ne[i]=j;
	}
	 for(int j=0,i=0;i<m;i++)
	{
	    while(j&&s[i]!=p[j]) j=ne[j-1];
	    if(s[i]==p[j]) j++;
	    if(j==n)
	    {
	        printf("%d ",i-n+1);
	        j=ne[j-1];
	    }
	}

14.字典树(数组模拟)

	//高效存储和查找字符串集合,关键在于存储
	int p=0;
	//从高位向低位存储,因为必须尽可能在前面的数字是相反的
	for(xxx)根据条件将每一位存入数组
	{
	    int n=x>>i&1;
	    if(!c[p][n]) c[p][n]=++idex;
	    p=c[p][n];//左边位存储需要的长度 右边位存储可能取的值
	}

15.并查集

	int find(int x)//找祖宗
	{
	    if(x!=s[x]) s[x]=find(s[x]);//x目前不是祖宗,把自己的值赋为祖宗
	    return s[x];
	}
	void merge(int a,int b)//已在同一个不需要合并
	{
	    int x=find(a);//一定要上升到祖宗而不是自己
	    int y=find(b);
	    s[x]=y;
	    siz[y]+=siz[x];
	}

16.堆(数组模拟)

//此处除了可能父子比较还可能同级比较 不能直接a[x]和a[2*x]
	void down(int x)//对x下标的值进行处理
	{
	    int j=x;
	    if(2*x<=siz&&a[j]>a[2*x]) j=2*x;
	    if(2*x+1<=siz&&a[j]>a[2*x+1]) j=2*x+1;
	    if(j!=x)
	    {
	        swap(a[j],a[x]);
	        down(j);
	    }
	}
	void up(int x)//对x下标的值进行处理
	{
	    while(x/2&&a[x]<a[x/2]) 
	    {
	        swap(a[x],a[x/2]);
	        x=x/2;
	    }
	}
	for(int i=n/2;i;i--) down(i);//形成最开始的小根堆
	while(m--)
	{
	    a[1]=a[siz--];//输出第一个后把第一个和最后一个进行交换
	    down(1);//其他都已经有序
	}
	//若非顶点进行删除,先将该点与最后一个点交换,再up(k); down(k);

17.哈希(数组模拟)

	//开放寻址法
	const int N=200003,null=0x3f3f3f3f;//合适的长度和为空的占位
	int a[N];//存储映射的
	int find(int x)//把返回值写成bool,应该是返回下标而不是返回能不能找到
	{
	   int t=(x%N+N)%N;
	   while(a[t]!=null&&a[t]!=x)//循环处理
	   {
	       t++;
	       if(t==N)
	        t=0;
	   }
	   return t;
	}
	memset(a, 0x3f, sizeof a);//按位填充数组
	a[find(x)]=x;//拉链法
	void insert(int x)//形成头插法链表 递减不一定连续下标
	{
	 int t=(x%N+N)%N;
	 e[idex]=x;
	 ne[idex]=a[t];
	 a[t]=idex++;
	}
	int find(int x)//把返回值写成bool,应该是返回下标而不是返回能不能找到
	{
	int t=(x%N+N)%N;
	for (int i = a[t]; i != -1; i = ne[i])
	     if (e[i] == x)
	         return true;
	 return false;
	}
	memset(a,-1, sizeof a);//按位填充数组

	//哈希的应用-字符串哈希
	const int P=1331;//为1331以此方法发生冲突可能性比较低
	cout<<c[r]-c[l-1]*p[r-l+1];//要右边加起来也为r
	for(int i=1;i<=n;i++)
	{
	    c[i]=P*c[i-1]+str[i-1];//和y总的读入不同
	    p[i]=P*p[i-1];
	}

18.DFS

	void dfs(int x)
	{
	   //根据题意设置终点状态
	   for(int i=1;i<=n;i++)
	   {
	       if(!b[i])
	       {
	           a[x]=i;//修改
	           b[i]=true;//标记
	           dfs(x+1);
	           b[i]=false;//还原标记
	       }
	   }
	}

19.BFS

	void bfs()
	{
	    queue<PII> s;//总是把队列和栈拼写写反
	    s.push({1,1});
	    memset(dis,-1,sizeof dis);
	    dis[1][1]=0;
	    int dx[4]={1,0,-1,0};
	    int dy[4]={0,1,0,-1};
	    while(s.size())
	    {
	        PII s1=s.front();
	        s.pop();
	        for (int i=0;i<4;i++) 
	        {
	            int x=s1.first+dx[i];
	            int y=s1.second+dy[i];
	            if (x>=1&&x<=n&&y>=1&&y<=m&&mg[x][y]==0&&dis[x][y]==-1) 
	            {
	                dis[x][y]=dis[s1.first][s1.second]+1;
	                s.push({x,y});
	            }
	        }
	    }

待定.质数、质因数、欧拉函数

	//质数-试除法
	bool is_prime(int x)
	{
	    if(x<2)
	        return false;
	    for(int i=2;i<=x/i;i++)
	        if(x%i==0) return false;
	    return true;
	}//质数-朴素筛
	void is_prime(int n)
	{
	    for(int i=2;i<=n;i++)//注意=n
	    {
	         if(!st[i])  
	         {
	             cnt++;//}也可以移动到这里
	             for(int j=i;j<=n;j+=i) st[j]=true;
	         }
	    }
	}//质数-线性筛- [欧拉函数]https://www.acwing.com/solution/content/28507/
	void is_prime(int n)
	{
		//ol[1]=1;存储某个数的欧拉函数
	    for(int i=2;i<=n;i++)
	    {
	        if(!st[i])  
	        {
	        	prime[cnt++] = i;
	        	//ol[i]=i-1;质数前的1-n个数都与自己是互质的
	        }
	        for(int j = 0; i*prime[j]<=n;j++)//方便看,把i移动
	        {
	            st[i*prime[j]]=true;//质数*合数一定是合数
	            //i>prime[j] 且合数i*prime[j]中prime[j]是最小素因子
	            if(i%prime[j]==0) 
	            {
	            	//ol[i*prime[j]]=ol[i]*prime[j];
	            	break;
	            }
	            //ol[i*prime[j]]=ol[i]*(prime[j]-1);
	        }      
	    }
	}
		
	//质因数-试除法
	void divide(int x)
	{
	   for(int i=2;i<=x/i;i++)
	       while(x%i==0) x=x/i;//计算欧拉函数ol=ol/i*(i-1);
	   	   if(x>1) //输出x 计算欧拉函数ol=ol/x*(x-1);
	}

待定.因数

	//求因数
	vector<int> get_divisors(int x)
	{
	    vector<int> res;
	    for(int i=1;i<=x/i;i++)
	    {
	        if(x%i==0)
	        {
	            res.push_back(i);
	            if(i!=x/i) res.push_back(x/i);
	        }
	    }
	    sort(res.begin(), res.end());
	    return res;
	}

    //通过unordered_map存入每个数值出现的次数
    long long sum=1;//注意这块需要设置为long long
    //因数个数
    for(auto item:m) 
    	sum=sum*(item.second+1)%N;
	//因数之和
	for(auto item:m) 
    {
        long long s=1;//注意这块也需要设置为long long
        while(item.second--) s=(s*item.first+1)%N;
        sum=s*sum%N;
    }

	//欧几里得
	int gcd(int a, int b)
	{
	     return a ?gcd(b%a,a): b;//a?--左边考虑为0--左边b%a--右边a 
	     return b ?gcd(b,a%b): a;//b?--右边考虑为0--左边a%b--左边b
	}
	
	//扩展欧几里得
	int kz_gcd(int a,int b,int &x,int &y)//通过引用一直得到实时的x,y
	{
	    if(b==0) 
	    {
	        x=1,y=0;
	        return a;
	    }
	    int x1,y1;
	    int gcd=kz_gcd(b,a%b,x1,y1);
	    x=y1,y=x1-a/b*y1;//y1不要随便换位置,可能有一定误差
	    //过程中除了a不关心x1、y1、gcd的变换 因为xy最后还是会变成1 0
	    return 0;
	  }

待定.快速幂

	void ksm(int a,int b,int p)
	{
	    long long m=1;
	    long aa=a;//由于a乘多次可能超限
	    while(b)
	    {
	        if(b&1) m=m*aa%p;
	        b=b>>1;
	        aa=aa*aa%p;//每一项都是先模再乘
	    }
	    cout<<m<<endl;
	}
	//根据费马小定理 快速幂求逆元需要满足a不是p的倍数 p是质数
	if(a%p!=0) ksm(a,p-2,p);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
算法图解-python.pdf》是一本介绍算法和使用Python语言实现算法的书籍。该书的目的是帮助读者了解算法的基本概念和原理,并通过Python编程实践来加深理解。 这本书以图解的方式呈现算法的思想和应用,使复杂的算法问题变得直观易懂。读者可以通过阅读该书,学习到各种常见的算法设计和解决思路,如排序算法、搜索算法、图算法等。同时,该书也会介绍一些高级算法,如动态规划、贪婪算法等,以及如何通过Python语言实现这些算法。 《算法图解-python.pdf》的内容结构清晰,通俗易懂。每个算法都有详细的解释和示例代码,读者可以通过实际编程来加深对算法的理解。此外,书中还包含了一些挑战性的练习题,供读者进一步巩固所学的知识。 通过阅读《算法图解-python.pdf》,读者不仅可以学习到算法的基本知识,还可以了解到如何运用Python语言实现这些算法。这对于刚开始学习算法和Python编程的读者来说是非常有帮助的。无论是计算机科学专业的学生,还是对算法感兴趣的爱好者,都可以从这本书中受益匪浅。 总之,《算法图解-python.pdf》是一本很好的算法入门书籍,以图解和Python编程为特色,适合各类读者学习和参考。通过阅读和实践,读者可以提高算法设计和编程实现的能力,为解决实际问题提供有效的思路和方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值