常用算法模板

本文详细介绍了各种编程算法,包括冒泡排序、求公约数、素数判断、前缀异或和、前缀和与差分数组、堆排序、二分查找、各种排序算法(快速排序、堆排序、归并排序、基数排序、希尔排序)、sort排序、回溯法解组合与排列问题、滑动窗口问题、并查集的应用、快速幂运算以及01背包问题和线段树的概念与实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

一、冒泡排序

  • 冒泡排序的模板

void maopao(int b[],int len){
   
   
	for(int i=0;i<len-1;i++){
   
   
		for(int j=0;j<len-i-1;j++){
   
   
			if(b[j]>b[j+1]){
   
   
				int t=b[j];
				b[j]=b[j+1];
				b[j+1]=t;
			}
		}
	}
} 

1、给定交换次数求最短字符串

  • 按字典序的通用模板
#include <iostream>
using namespace std;
int main()
{
   
   
  // 请在此输入您的代码
  string str;
  int k=100;    //交换次数
  int n=0;      //字符串长度
  for(;k>n*(n-1)/2;n++);      //求n的大小   冒泡排序的交换次数公式n*(n-1)/2
  str+='a'+k-(n-1)*(n-2)/2;   //求第一个字符
  for(int i='a'+n-1;i>='a';i--){
   
   
    if(i!=str[0]){
   
               //完全逆序————当等于第一个字符时跳过
      str+=i;
    }
  }
  cout<<str;
  return 0;
}

二、公约数

1、给定值n求abc之积=n的个数

  • 排列——即考虑顺序
  • 思路:现求n的所有约数,用数组存储,之后遍历数组即可
#include <iostream>
using namespace std;
int main()
{
   
   
  // 请在此输入您的代码
  long long n=2021041820210418;
  int res=0;
  int cnt=0;
  long long arr[1000000];

  for(long long i=1;i*i<=n;i++){
   
               //求n的所有约数,并用数组arr保存,注意此处的循环结束条件——i*i
    if(n%i==0){
   
   
      arr[cnt++]=i;
      if(n/i!=i){
   
                        //一次存放除数和被除数
        arr[cnt++]=n/i;
      }
    }
  }

  for(long long i=0;i<cnt;i++){
   
                   //遍历存放约数的数组
    for(long long j=0;j<cnt;j++){
   
   
      for(long long k=0;k<cnt;k++){
   
   
        if(arr[i]*arr[j]*arr[k]==n)res++;      //将约数组合,若之积为n则res+1
      }
    }
  }
  cout<<res;
  return 0;
}

注意:在求n的约数时,循环结束的条件是 i * i

2、求最大公约数

递归版辗转相除法

int gcd(int a,int b)
{
   
   
  if(a<b)           	//保证a大于b
  {
   
   
    swap(a,b);
  }
  if(a%b == 0)
  {
   
   
    return b;
  }
  return gcd(b,a%b);	//除数和余数作为参数继续运算
}

三、素数

1、判断是否为素数

//判断是否是素数

int f(int x){
   
   
    for(int i=2;i<x;i++){
   
   
        if(x%i==0)
        return 0;
    }
    return 1;
} 

2、给定长度求等差素数的公差

#include <iostream>
using namespace std;

bool check(int n){
   
                   //判断素数
  for(int i=2;i<n;i++){
   
   
    if(n%i==0)return false;
  }
  return true;
}
int main()
{
   
   
  // 请在此输入您的代码
  int cnt=1;                      //等差数列的长度
  int nn=10;                      //规定的等差数列长度
  for(int i=2;i<10000;i++){
   
          //先确定等差数列的第一个值
    if(check(i)){
   
                    //若为素数,便将其假设为结果的第一个素数
      for(int d=1;d<1000;d++){
   
      //尝试公差,由大到小依次尝试
        while(check(i+cnt*d)){
   
       //以当前公差,尝试求连续的10个素数
          cnt++;
          if(cnt==nn){
   
   
            cout<<d;
            return 0;
          }
        }                 
        cnt=1;                    //若不满足便有效等差素数回到原值
      }
    }
  }
  return 0;
}

四、前缀异或和

1、求子数组两两减去2的k次方后变为0

  • 由于每次操作要减去两个数的 2的k次方,那么所有数中,2的k次方出现的次数之和必须是偶数。换一种说法,就是所有数的异或和必须是 0

例题:给你一个下标从 0 开始的整数数组nums 。每次操作中,你可以:

选择两个满足 0 <= i, j < nums.length 的不同下标 i 和 j 。
选择一个非负整数 k ,满足 nums[i] 和 nums[j] 在二进制下的第 k 位(下标编号从 0 开始)是 1 。
将 nums[i] 和 nums[j] 都减去 2k 。
如果一个子数组内执行上述操作若干次后,该子数组可以变成一个全为 0 的数组,那么我们称它是一个 美丽 的子数组。

请你返回数组 nums 中 美丽子数组 的数目。

子数组是一个数组中一段连续 非空 的元素序列。

class Solution {
   
   
public:
    long long beautifulSubarrays(vector<int>& nums) {
   
   
        int n = nums.size();
        unordered_map<int, int> cnt;
        cnt[0] = 1;

        int now = 0;
        long long ans = 0;
        for (int i = 0; i < n; i++) {
   
   
            now ^= nums[i];             //求前缀异或
            ans += cnt[now];            //有几个相等的前缀异或值,便有几个满足要求的子数组
            cnt[now]++;                 //记录前缀异或相等的值的个数
        }
        return ans;
    }
};


四、前缀和和差分数组

1、前缀和

1.1、一维前缀和

使用前缀和的情况:
	求连续的和
解决办法:使用一维数组s[i]记录前i项和,若是求[L,R]区间的值,则s[R]-s[L-1];

前i项和的代码

for(int i=1;i<n){
   
   
	cin>>s[i];
	s[i]+=s[i-1];
}

例题:给定连续的数值,允许跳过k段距离的数,求剩余数值和的最小的情况
解题步骤:

  1. 先求前缀和
  2. 求区间为k的最大值
for(int i=k;i<n;i++){
   
   
	long long res=0;
	res=sum[i]-sum[i-1];
	maxx=max(res,maxx);
}
  1. 最终结果s[n-1]-maxx;

1.2、二维前缀和

  • s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+s[i][j];
  • 在这里插入图片描述
  • 求某个子矩阵所有数值的和
  • 所求=s[x2][y2]-s[x1][y2]-s[x2][y1]+s[x1][y1]
  • 子矩阵的边长=x2-x1,y2-y1;
  • 在这里插入图片描述
  • 例题:此题规定了边长长度为c在这里插入图片描述
  • 例题二:不固定边长求子矩阵累加和最大的那个
  • 在这里插入图片描述

2、差分算法

  1. 求差分数组

void insert(int l, int r, int c)
{
   
   
    b[l] += c;
    b[r+1] -= c;
}

  1. 首先计算起始成分数组,for循环遍历每一项
for(int i = 1; i <= n; i ++)
    {
   
   
        insert(i,i,a[i]);
    }
  1. 若是需对原数组某个区段[L,R]加减值时,只需让差分数组第L位加上该值,第R+1位减去该值,再求前缀和便可得到改变后的数组
while(m--)				//m代表操作m次
    {
   
   
        int l,r,c;		//l和r代表区间的左右边界——c代表操作的值
        scanf("%d%d%d",&l,&r,&c);
        insert(l,r,c);
    }
    for(int i = 1; i <= n; i++)	
    {
   
   
        b[i] += b[i-1];	//求差分数组每项的前缀和,便得到对应下标数组的值
        printf("%d ",b[i]);
    }

例题:在这里插入图片描述

  • 关键:比较列出关系式——min(A\*n,B*n+c)
  • n为对应路段经过的次数——将先后输出的城市序号当作区间的左右边界,其中经过的路段在原基础上+1——刚好满足差分思想
#include<bits/stdc++.h>
using namespace std;
int a[1005];		//存储原数组——可有可无 
int b[1005];		//记录差分数组——此处使用一个数组,先表示差分数组,进行前缀和后表示经过变化后的数组 

void in
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值