我的算法模板

引言:不同的人模版不同,程序是人思维的结晶,最好用自己的代码,殊途同归!

数据结构篇

1.并查集

    在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。

//并查集(路径压缩)
const int max_n=100005;
int par[max_n]; 

void init(int n){  //初始化
    for(int i=1;i<=n;i++) par[i]=i;  
}  
  
int find(int x){  //查找x所在集合的根
    if(par[x]!=x) par[x]=find(par[x]);//递归返回的同时压缩路径
    return par[x];  
}  
  
void unite(int x,int y){  //合并x与y所在集合
    x=find(x);  
    y=find(y);  
    par[x]=y; 
}  
  
bool same(int x,int y){  //x与y在同一集合则返回真
    return find(x)==find(y);  
} 

2.字典树

    处理大量字符串

//一个以链表实现带删除功能允许重复字符串的字典树
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


int charmapping[256]; //字符映射数组,charmapping[i]=x表示ascii码为i的字符对应于treenode中的next[x] 
void init_charmapping(){
	for(int i='a';i<='z';i++){ //我的这个字典树现在只允许输入小写字符组成的字符串,然而由于有charmapping的存在,增加新字符添加映射并且增大maxn就好,很方便. 
		charmapping[i]=i-'a';
	} 
} 

const int maxn=26; //这里假设字符串中只出现26个小写字母 
const int maxm=100000;
struct treenode{
	int count; //标志此节点所表示字符串在所有字符串中以前缀形式出现的总次数 
	treenode* next[maxn]; 
}head;

void init_trie(){
	head.count=1; //初始化为1包括空串并且避免树头被删 
	for(int i=0;i<maxn;i++) head.next[i]=NULL;
}

treenode* createnew(){ //申请一个新结点并初始化它
	treenode* newnode;
	newnode=(treenode*)malloc(sizeof(treenode));
	newnode->count=0;
	for(int i=0;i<maxn;i++) newnode->next[i]=NULL;
	return newnode;
}

void update(char* s,int num){ //向字典树添加num个字符串s 
	int k=0,temp;
	treenode* t=&head;
	while(s[k]){
		t->count+=num;
		temp=charmapping[s[k]];
		if(!t->next[temp]) t->next[temp]=createnew(); 
		t=t->next[temp];
		k++;
	}
	t->count+=num;
}

bool search(char* s,int num){  //查找字典树中是否已经存在num个字符串s
	int k=0,temp;
	treenode* t=&head;
	while(s[k]){
		temp=charmapping[s[k]];
		if(!t->next[temp]||t->next[temp]->count<num) return false; //根本不存在字符串s或者存在的数目小于num直接失败 
		t=t->next[temp];
		k++;
	}
	int snum=t->count;
	for(int i=0;i<maxn;i++) if(t->next[i]) snum-=t->next[i]->count; //这里是核心!!!结点t代表的字符串出现的次数就是总次数减去所有子节点次数和 
	if(snum>=num) return true; //如果字符串s的数目snum大于等于num 
	return false;
}

void erase(char* s,int num){  //删除字典树中的num个字符串s并释放无用结点,删除前一定要先search是否存在 
	int k=0,temp;
	treenode* t=&head;
	treenode* t1; //t1后面的结点都是删除后需要被释放的 
	head.count-=num;
	while(s[k]){
		temp=charmapping[s[k]];
		t->next[temp]->count-=num;
		if(t->next[temp]->count==0){
			t1=t->next[temp];
			t->next[temp]=NULL;
			k++;
			break;
		}
		t=t->next[temp];
		k++;
	}
	while(s[k]){ //释放无用结点 
		temp=charmapping[s[k]];
		t=t1->next[temp];
		free(t1);
		t1=t;
		k++;
	}
	free(t1);
}

char temp[1000];
void printall(treenode* tnode,int pos){ //递归打印字典树咯,打出了就是字典序升序的 
	int count=tnode->count;
	for(int i=0;i<maxn;i++) if(tnode->next[i]) count-=tnode->next[i]->count;
	for(int i=0;i<count;i++) printf("\"%s\"\n",temp);
	for(int i='a';i<='z';i++){
		if(tnode->next[charmapping[i]]){
			temp[pos]=i;
			temp[++pos]='\0';
			printall(tnode->next[charmapping[i]],pos);
			temp[--pos]='\0';
		}
	}
}

int main(){
	init_charmapping(); //初始化映射 
	init_trie();		//初始化字典树 
	char x[1000];
	char order; //命令 
	int num;    //数目 
	printf("q:查询\nu:插入\nd:删除\np:打印字典树\ne:退出\n");
	while(1){
		printf("请输入命令:");
		fflush(stdin);
		scanf("%c",&order);
		if(order=='q'){
			printf("请输入要查找的字符串与数目:");
			scanf("%s%d",&x,&num);
			if(search(x,num)) printf("匹配成功。\n\n");
			else printf("匹配失败,不存在%d个\"%s\"\n\n",num,x); 
		}
		else if(order=='u'){
			printf("请输入要插入的字符串与数目:");
			scanf("%s%d",&x,&num);
			update(x,num);
			printf("%d个\"%s\"已加入字典树。\n\n",num,x);
		}
		else if(order=='d'){
			printf("请输入要删除的字符串与数目:");
			scanf("%s%d",&x,&num);
			if(!search(x,num)){
				printf("树中无%d个字符串\"%s\"请重新键入命令!\n\n",num,x);
				continue;
			}
			erase(x,num);
			printf("%d个\"%s\"已从字典树中删除。\n\n",num,x);
		}
		else if(order=='p'){
			printf("当前字典树内有如下字符串:\n");
			temp[0]='\0';
			printall(&head,0);
		}
		else if(order=='e'){
			printf("退出ing....\n"); 
			break;
		}
		else printf("无效命令,请重新输入!\n命令q:查询是否存在字符串\n命令u:往字典树加入字符串\n命令d:删除某个字符串\n命令p:按字典序升序输出字典树\n命令e:退出程序\n\n"); 
	} 
	return 0;
}

3.线段树与树状数组

    RMQ,区间求和,区间状态求解这些问题

//树状数组BIT
/*
作用:
1.给定i计算1到i的和
2.给定i和x,执行ai+=x;
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std; 
const int maxn=10000;
int bit[maxn+1],n;

int sum(int i){
	int s=0;
	while(i>0){
		s+=bit[i];
		i-=i&-i;
	}
	return s;
}

void add(int i,int x){
	while(i<=n){
		bit[i]+=x;
		i+=i&-i;
	}
}

void init(int tn){
	n=1;
	while(n<tn) n=(n<<1);
	memset(bit,0,2*n-1);
	int t;
	for(int i=1;i<=tn;i++){
		scanf("%d",&t);
		add(i,t);
	}
}

int main(){
	scanf("%d",&n);
	init(n);
	for(int i=1;i<=n;i++) cout<<bit[i]<<" ";
	cout<<endl;
	return 0;
}

 

算法篇

1.KMP算法

    涉及字符串匹配的问题.

//kmp算法
const int maxn=100005;
int next[maxn];//next数组 
void getnext(char* s){//构造next数组,真正的模版
	next[0]=-1;
	int i=0,j=-1; //j为什么初值赋值-1?0其实也行,仅仅是为了少一个判断, 
	while(s[i]){
		if(j==-1||s[i]==s[j]) next[++i]=++j; 
		else j=next[j];
	}
}

void kmp(char* a,char* b){ //输出a中每个匹配b串的下标,不同问题这个函数的写法多变
	int blen=0;
	while(b[blen]) blen++;
	getnext(b);
	int i=0,j=0;
	while(a[i]){
		if(j==-1||a[i]==b[j]){
			i++,j++;
			if(!b[j]){
				printf("%d ",i-blen);
				j=next[j];  
			}
		}
		else j=next[j];  
	}
}

2.素数处理

     素数相关的问题.

// 位操作求素数 
const int maxn=1000000;
int prime[maxn],primenum;
int flag[maxn/32+1];//数组大小实际缩小8倍 
void wei_prime(int t){
	primenum=0;
	flag[0]|=3;
	for(int k=1;k<=t/32+1;k++) flag[k]=0;
	for(int i=2;i<=t;i++){
		if(!((flag[i/32]>>(i%32))&1)){
			for(int j=i*2;j<=t;j+=i) flag[j/32]|=(1<<(j%32));
			prime[primenum++]=i;
		}	
	}
}
void wei_prime_onlyflag(int t){
	flag[0]|=3;
	for(int k=1;k<=t/32+1;k++) flag[k]=0;
	for(int i=2;i<=t;i++) if(!((flag[i/32]>>(i%32))&1)) for(int j=i*2;j<=t;j+=i) flag[j/32]|=(1<<(j%32));
}
void printby_prime(){
	for(int i=0;i<primenum;i++) printf("%d ",prime[i]);
}
void printby_flag(int t){
	for(int i=0;i<=t;i++) if(!((flag[i/32]>>(i%32))&1)) printf("%d ",i);
}

int main(){
	int t=100;
	wei_prime(t);
	printby_prime();
	printf("\n");
	printby_flag(t);
	return 0;
}

3.gcd与扩展gcd

    gcd算法是一个很古老的用于计算两数最大公约数的算法,而扩展gcd是基于gcd的一个扩展算法,用于求解模线性方程ax≡b (mod n).

//gcd与扩展gcd
int gcd(int a.int b){ //返回a,b的最大公因数
	if(b==0) return a;
	return gcd(b,a%b);
}

ll extgcd(ll a,ll b,ll &x,ll &y){ //扩展gcd
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	ll d=extgcd(b,a%b,x,y);
	ll t=x;
	x=y;
	y=t-a/b*y;
	return d;
}

ll cal(ll a,ll b,ll c){ //计算ax+by=c的满足条件的x 
    ll x,y;
    ll gcd=extgcd(a,b,x,y);
    if(c%gcd!=0) return -1; //不存在解 
    x*=c/gcd;
    b/=gcd;
    if(b<0) b=-b;
    ll ans=x%b;
    if(ans<=0) ans+=b; //对ans为负的特殊处理+ 
    return ans;
}

猜你喜欢

转载自blog.csdn.net/qq_31964727/article/details/76791743