我的算法模板(帶分析博客)

目錄

引言:不同的人模版不同,程序是人思維的結晶,最好用自己的代碼,殊途同歸!

(以下模版均有其對應的分析博客,點擊標題即可跳轉)

數據結構篇

1.並查集

2.字典樹

3.線段樹與樹狀數組

          4.ac自動機

5.大根堆

算法篇

1.KMP算法

2.素數處理

3.gcd與擴展gcd

4.二分查值法

(1).最大化最小值

(2).最小化最大值

5.排序算法合集(模版僅摘選幾個高效的)

(1).插入排序

(2).冒泡排序

(3).歸併排序

(4).堆排序

(5).快速排序

(6).計數排序

(7).基數排序

6.快速冪算法

待整理的代碼


 

數據結構篇

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>
const int maxn=26; //這裏假設字符串中只出現26個小寫字母 
const int maxm=100000;
struct treenode{
	bool end; //標誌此節點是否是某字符串的結尾 
	treenode* next[maxn];
}head;

treenode memory[maxm];
int mallocp=0;

void init(){
	head.end=1;
	for(int i=0;i<maxn;i++) head.next[i]=NULL;
}

treenode* createnew(){
	treenode* newnode;
	newnode=&memory[mallocp++];
	newnode->end=0;
	for(int i=0;i<maxn;i++) newnode->next[i]=NULL;
	return newnode;
}

void update(char* s){
	int k=0,temp;
	treenode* t=&head;
	while(s[k]){
		temp=s[k]-97;
		if(!t->next[temp]) t->next[temp]=createnew(); 
		t=t->next[temp];
		k++;
	}
	t->end=1;
}

bool search(char* s){
	int k=0,temp;
	treenode* t=&head;
	while(s[k]){
		temp=s[k]-97;
		if(!t->next[temp]) return false;
		t=t->next[temp];
		k++;
	}
	if(t->end) return true;
	return false; 
}

int main(){
	init();
	char x[1000];
	char t;
	while(1){
		fflush(stdin);
		scanf("%c",&t);
		if(t=='q'){
			scanf("%s",&x);
			if(search(x)) printf("匹配成功!\n");
			else printf("匹配失敗!\n"); 
		}
		else if(t=='u'){
			scanf("%s",&x);
			update(x);
			printf("更新完畢!\n");
		}
		else if(t=='e'){
			printf("退出ing....\n"); 
			break;
		}
		else printf("無效命令!,請重新輸入!\n"); 
	} 
	return 0;
}

//一個以鏈表實現帶刪除功能允許重複字符串的字典樹
#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,區間求和,區間狀態求解這些問題

//線段樹 
/*
常見使用方法: 

一.基於線段樹的RMQ
要求完成:
1.給定s和t,求s到t區間內的最小值(求最大值只需修改部分細節) 
2.給定i,x把a[i]的值修改爲x
*/
#include <iostream>
#include <string.h>
using namespace std;
const int maxn=10000;
const int inf=(1<<30);
int n;
int dat[4*maxn];
int min(int a,int b){
	if(a>b) return b;
	return a;
}
void update(int k,int x){
	k+=n-2;
	dat[k]=x;
	while(k>0){
		k=(k-1)>>1;
		dat[k]=min(dat[(k<<1)+1],dat[(k<<1)+2]);
	}
}

void init(int tn){
	n=1;
	while(n<tn) n=(n<<1);
	memset(dat,inf,2*n+1);
	int t;
	for(int i=1;i<=n;i++){
		scanf("%d",&t);
		update(i,t);
	}
}

int query(int a,int b,int k,int l,int r){
	if(a>r||b<l) return inf;
	if(a<=l&&b>=r) return dat[k];
	else{
		int v1=query(a,b,(k<<1)+1,l,(l+r)>>1);
		int v2=query(a,b,(k<<1)+2,((l+r)>>1)+1,r);
		return min(v1,v2);
	}
}

int main(){
	cin>>n;
	init(n);
	int t1,t2;
	char ch;
	getchar();
	while(1){
		scanf("%c%d%d",&ch,&t1,&t2);
		if(ch=='Q') printf("%d\n",query(t1,t2,0,1,n));
		else if(ch=='U') update(t1,t2);
		getchar();
	}
	return 0;
}
 
 /*
二. 求指定區間和
1.求指定區間l到r的和
2.對第i個加x
*/
#include <iostream>
#include <string.h>
using namespace std;
const int maxn=10000;
int n;
int dat[4*maxn];

void update(int k,int x){
	k+=n-2;
	dat[k]=x;
	while(k>0){
		k=(k-1)>>1;
		dat[k]=dat[(k<<1)+1]+dat[(k<<1)+2];
	}
}

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

int query(int a,int b,int k,int l,int r){
	if(a>r||b<l) return 0;
	if(a<=l&&b>=r) return dat[k];
	else{
		int v1=query(a,b,(k<<1)+1,l,(l+r)>>1);
		int v2=query(a,b,(k<<1)+2,((l+r)>>1)+1,r);
		return v1+v2;
	}
}

int main(){
	cin>>n;
	init(n);
	int t1,t2;
	char ch;
	getchar();
	while(1){
		scanf("%c%d%d",&ch,&t1,&t2);
		if(ch=='Q') printf("%d\n",query(t1,t2,0,1,n));
		else if(ch=='U') update(t1,t2);
		getchar();
	}
	return 0;
}

//樹狀數組BIT
/*
作用:
1.給定i計算1到i的和
2.給定i和x,執行ai+=x;
*/
#include <iostream>
#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(n){
	int tn=n,t;
	n=1;
	while(n<tn) n=(n<<1);
	memset(bit,0,2*n-1);
	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;
}

//線段樹的區間更新(延遲標記)

const int maxn=100000;
int treen,tl,tr,dat[4*maxn],add[4*maxn];

void init(int tn){
	treen=1;
	while(treen<tn) treen<<=1;
	memset(dat,0,sizeof(dat));
	memset(add,0,sizeof(add));
}
 
void pushdown(int k,int l,int r){
	int temp=(r-l+1)>>1;
	add[(k<<1)+1]+=add[k];
	dat[(k<<1)+1]+=temp*add[k];
	add[(k+1)<<1]+=add[k];
	dat[(k+1)<<1]+=temp*add[k];
	add[k]=0;
}
 
void update(int k,int l,int r,int val){
	if(r<tl||l>tr) return ;
	if(r<=tr&&l>=tl){
		dat[k]+=val*(r-l+1);
		add[k]+=val;
		return ;
	}
	update((k<<1)+1,l,(l+r)>>1,val);
	update((k+1)<<1,((l+r)>>1)+1,r,val);
}
 
int query(int k,int l,int r){
	if(r<tl||l>tr) return 0;
	if(r<=tr&&l>=tl) return dat[k];
	if(add[k]) pushdown(k,l,r);
	return query((k<<1)+1,l,(l+r)>>1)+query((k+1)<<1,((l+r)>>1)+1,r);
}

4.ac自動機

    求解字符串多模匹配問題

#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <malloc.h>
using namespace std;
const int maxkeywordlen=51;
const int maxstrlen=1000001; 
char keyword[maxkeywordlen],str[maxstrlen];
 
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';
	} 
}
 
struct node{
    node *fail;     //失敗指針,此節點失配時將跳向fail所指節點 
	int end;        //此值表示以此節點爲結尾的單詞個數 
	node *next[26]; //指向子節點 
}root;
 
node *getnode(){  //構造 一個新結點並做好初始化,返回指向該節點的指針 
	node *newnode=(node *)malloc(sizeof(node));
	newnode->end=0;
	newnode->fail=NULL;
    for(int i=0;i<26;i++) newnode->next[i]=NULL;
    return newnode;
}
 
void insert(char *s){ //向ac自動機加入字符串s 
    int k=0,temp;
    node* t=&root;
	for(int i=0;s[i];i++){
		temp=charmapping[s[i]];
        if(!t->next[temp]) t->next[temp]=getnode();
        t=t->next[temp];
	}
    t->end++;
}
 
void build_fail(){ //構造ac自動機的失敗指針 
	queue<node *> q;
    q.push(&root);
    while(!q.empty()){
        node* t=q.front();
        q.pop();
        for(int i=0;i<26;i++){
            if(t->next[i]){
                if(t==&root) t->next[i]->fail=&root;
                else{
                    node *p=t->fail;
                    while(p!=NULL){
                        if(p->next[i]){
                            t->next[i]->fail=p->next[i];
                            break;
                        }
                        p=p->fail;
                    }
                    if(p==NULL) t->next[i]->fail=&root;
                }
                q.push(t->next[i]);
            }
        }
    }
}
 
void free_trie(){ //釋放ac自動機, 只留根節點 
	queue<node *> q;
	for(int i=0;i<26;i++){
		if(root.next[i]){
			q.push(root.next[i]);		
			root.next[i]=NULL;
		}
	}
    while(!q.empty()){
        node* t=q.front();
        q.pop();
        for(int i=0;i<26;i++) if(t->next[i]) q.push(t->next[i]); 
        free(t);
    }
}
 
int match(char *s){ //計算ac自動機中所有模式串在字符串s中出現的次數之和 
    node *now=&root;
	int ans=0;
    for(int i=0;s[i];i++){
        int temp=charmapping[s[i]];
        if(now->next[temp]) now=now->next[temp];
        else{
            node* p=now->fail;
            while(p!=NULL&&p->next[temp]==NULL) p=p->fail;
            if(p==NULL) now=&root;
            else now=p->next[temp];
        }
        if(now->end){
            node *tn=now;
            while(tn!=NULL){
                ans+=tn->end;
                tn->end=0;
                tn=tn->fail;
            }
        } 
    }
    return ans;
}

int main(){
	init_charmapping();
	int t;
	scanf("%d",&t);
	while(t--){
		int n;
		scanf("%d",&n);
		for(int i=0;i<n;i++){
			scanf("%s",&keyword);
			insert(keyword);
		}
		build_fail();
		scanf("%s",&str);
		printf("%d\n",match(str));
		free_trie();
	}
	return 0;
} 

5.大根堆

const int maxn=100001;
class myheap_max{
	private:
		int heap[maxn];
		int heap_size;
	public:
		void init(){
			for(int i=0;i<maxn;i++) heap[i]=0;
			heap_size=0;
		}
		void clear(){
			for(int i=0;i<heap_size;i++) heap[i]=0;
			heap_size=0;
		}
		void myswap(int *t1,int *t2){ int temp=*t1;*t1=*t2;*t2=temp; } //交換函數,相當於swap 
		int parent(int pos){ return (pos-1)/2; }        //獲得pos結點的父親節點 
		int left_children(int pos){ return pos*2+1; }   //獲得pos結點的左子節點 
		int right_children(int pos){ return pos*2+2; }//獲得pos結點的右子節點 
		
		int size(){ return heap_size; }
		bool empty(){ return heap_size==0; }
		
		void push(int t){ //push操作 
			int now=heap_size++;
			do{
				int pa=parent(now); 
				if(heap[pa]>=t) break;
				heap[now]=heap[pa];
				now=pa; 
			}while(now);
			heap[now]=t;
		}
		
		int top(){ //top操作 
			return heap[0];
		}
		
		void adjust_max(int pos){ //調整使pos結點下的子樹成爲一個大根堆 
			int l=left_children(pos),r=right_children(pos),largest;
			if(l<heap_size&&heap[l]>heap[pos]) largest=l;
			else largest=pos;
			if(r<heap_size&&heap[r]>heap[largest]) largest=r;
			if(largest!=pos){
				myswap(heap+largest,heap+pos); 
				adjust_max(largest);
			}
		}
		
		void pop(){ //pop操作 
			heap[0]=heap[--heap_size];
			adjust_max(0); 
		} 
};

或者

priority_queu<int> que;

 

算法篇

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*i;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_flag(int t){
	for(int i=0;i<=t;i++) if(!((flag[i/32]>>(i%32))&1)) printf("%d ",i);
}

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;
}

4.二分查值法

用於求滿足某條件的最大化最小值的答案或最小化最大值答案.

(1).最大化最小值

bool c(double mid){
    //判斷答案爲mid時是否滿足條件,滿足返回true,否則false
}
 
void solve(){
	double l=0,r=maxa,mid;
	while(r-l>0.001){
		mid=(l+r)/2;
		if(c(mid)) l=mid; 
		else r=mid;
	}
	printf("%.1lf",l);
}

(2).最小化最大值

bool c(double mid){
    //判斷答案爲mid時是否滿足條件,滿足返回true,否則false
}
 
void solve(){
	double l=0,r=maxa,mid;
	while(r-l>0.001){
		mid=(l+r)/2;
		if(c(mid)) r=mid; 
		else l=mid;
	}
	printf("%.1lf",r);
}

5.排序算法合集(模版僅摘選幾個高效的)

(1).插入排序

(2).冒泡排序

(3).歸併排序

void merge(int *a,int l,int r,int mid){
	int temp[r-l],tempnum=0;
	int pos1=l,pos2=mid+1;
	while(pos1<=mid&&pos2<=r){
		if(a[pos1]<=a[pos2]) temp[tempnum++]=a[pos1++];
		else temp[tempnum++]=a[pos2++];
	}
	while(pos1<=mid) temp[tempnum++]=a[pos1++];
	while(pos2<=r) temp[tempnum++]=a[pos2++];
	for(int i=0;i<tempnum;i++) a[l+i]=temp[i];
} 

void merge_sort(int *a,int l,int r){ //排序a數組中[l,r]區間內的數爲升序 
	if(l<r){
		int mid=(l+r)/2;
		merge_sort(a,l,mid);
		merge_sort(a,mid+1,r);
		
		merge(a,l,r,mid);
	}
}

(4).堆排序

const int maxn=100001;
class myheap_max{
	private:
		int heap[maxn];
		int heap_size;
	public:
		void init(){
			for(int i=0;i<maxn;i++) heap[i]=0;
			heap_size=0;
		}
		void clear(){
			for(int i=0;i<heap_size;i++) heap[i]=0;
			heap_size=0;
		}
		void myswap(int *t1,int *t2){ int temp=*t1,*t1=*t2,*t2=temp; } //交換函數,相當於swap 
		int parent(int pos){ return (pos-1)/2; }        //獲得pos結點的父親節點 
		int left_children(int pos){ return pos*2+1; }   //獲得pos結點的左子節點 
		int right_children(int pos){ return pos*2+2; }//獲得pos結點的右子節點 
		
		int size(){ return heap_size; }
		bool empty(){ return heap_size==0; }
		
		void push(int t){ //push操作 
			int now=heap_size++;
			do{
				int pa=parent(now); 
				if(heap[pa]>=t) break;
				heap[now]=heap[pa];
				now=pa; 
			}while(now);
			heap[now]=t;
		}
		
		int top(){ //top操作 
			return heap[0];
		}
		
		void adjust_max(int pos){ //調整使pos結點下的子樹成爲一個大根堆 
			int l=left_children(pos),r=right_children(pos),largest;
			if(l<heap_size&&heap[l]>heap[pos]) largest=l;
			else largest=pos;
			if(r<heap_size&&heap[r]>heap[largest]) largest=r;
			if(largest!=pos){
				myswap(heap+largest,heap+pos); 
				adjust_max(largest);
			}
		}
		
		void pop(){ //pop操作 
			heap[0]=heap[--heap_size];
			adjust_max(0); 
		} 
};

void heap_sort(int *a,int l,int r){
	myheap_max que;
	que.init();
	for(int i=l;i<=r;i++) que.push(a[i]);
	for(int i=r;i>=l;i--){
		a[i]=que.top();
		que.pop();
	}
}

(5).快速排序

void quick_sort(int *a,int l,int r){
	if(l>=r) return;
	int key=a[l],left=l,right=r;
	while(left<right){
		while(a[right]>=key&&left<right) right--;
		while(a[left]<=key&&left<right) left++;
		if(left<right){
			int t=a[left];
			a[left]=a[right];
			a[right]=t;	
		}
	}
	a[l]=a[left];
	a[left]=key;
	quick_sort(a,l,left-1);
	quick_sort(a,left+1,r);
}

(6).計數排序

const int tmin=0;       //輸入數據最小界 
const int tmax=1000001; //輸入數據最大界 
const int k=tmax-tmin+1;//區間大小 
int count[k];  //這裏count[i]存的是輸入數據中i+tmin出現的次數 
void jishu_sort(int *a,int l,int r){
	for(int i=0;i<k;i++) count[i]=0;
	for(int i=l;i<=r;i++) count[a[i]-tmin]++;
	int pos=0;
	for(int i=0;i<k;i++){
		if(count[i]){
			for(int j=0;j<count[i];j++) a[pos+j]=i+tmin;
			pos+=count[i];
		}	
	}
}

(7).基數排序

 
#include <stdio.h>
 
const int k=10;//關鍵碼區間大小 
void radix_sort(int* a,int l,int r,int key){ //排序a數組區間[l,r]內的數字
	if(key<1||r-l+1<2) return;
	int count[k];  //這裏count[i]存的是輸入數據中i出現的次數 
	for(int i=0;i<k;i++) count[i]=0;
	for(int i=l;i<=r;i++) count[a[i]/key%10]++;
	for(int i=1;i<k;i++) count[i]+=count[i-1];
	
	int tcount[k]; //count複製數組 
	for(int i=0;i<k;i++) tcount[i]=count[i];
	int temp[r-l]; //暫存排序結果 
	for(int i=l;i<=r;i++) temp[--tcount[a[i]/key%10]]=a[i]; //一種計數排序的思維 
	for(int i=0;i<r-l+1;i++) a[l+i]=temp[i]; //上傳排序結果到a 
	
	//對關鍵碼0-9分出的區間分別進行基數排序 
	radix_sort(a,0,count[0]-1,key/10);
	for(int i=1;i<k;i++) radix_sort(a,count[i-1],count[i]-1,key/10); 
}
 
 
int main(){
	int a[10]={13,55,25,36,66,38,51,29,114,157};
	int key=1;
	for(int i=0;i<10;i++) while(key<a[i]) key*=10;
	radix_sort(a,0,9,key/10);
	for(int i=0;i<10;i++) printf("%d ",a[i]);
	printf("\n");
	return 0;
}

6.快速冪算法

ll fastpower(ll a,ll b,ll mod)//計算(a^b)%mod 
{    
    ll ans=1;
    ll powerofa=a;     
    while(b){
       if(b&1) ans=ans*powerofa%mod;//一種位運算 
       powerofa=powerofa*powerofa%mod;     
       b>>=1;
    }
    return ans;     
}

 

待整理代碼

//用鄰接表和鄰接矩陣存圖. 基於鄰接表的拓撲排序 
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int inf=0x3f3f3f3f;
int main(){
	int v,e,x,y;
	cin>>v>>e;
	int a[v+1][v+1];
	for(int i=0;i<=v;i++) for(int j=0;j<=v;j++) a[i][j]=i==j?0:inf;
	vector<int> m[v+1];
	while(e--){
		cin>>x>>y;
		a[x][y]=1;
		m[x].push_back(y);
	}
	for(int i=1;i<=v;i++){
		for(int j=1;j<=v;j++) cout<<a[i][j]<<" ";
		cout<<endl;
	}
	for(int i=1;i<=v;i++){
		cout<<i<<"->";
		for(int j=0;j<m[i].size();j++) cout<<m[i][j]<<" ";
		cout<<endl;
	}
	
	int rudu[v+1];
	for(int i=0;i<v+1;i++) rudu[i]=0;
	for(int i=1;i<=v;i++) for(int j=0;j<m[i].size();j++) rudu[m[i][j]]++;
	queue<int> q;
	for(int i=1;i<=v;i++) if(!rudu[i]) q.push(i);
	int tuopuans[v];
	int count=0,t,flag[v+1];
	for(int i=1;i<=v;i++) flag[i]=0;
	while(!q.empty()){
		t=q.front();
		q.pop();
		if(flag[t]) break;
		else flag[t]=1;
		tuopuans[count++]=t;
		for(int i=0;i<m[t].size();i++) if(--rudu[m[t][i]]==0) q.push(m[t][i]);
	}
	if(count!=v) cout<<"wrong"<<endl;
	else for(int i=0;i<v;i++) cout<<tuopuans[i]<<" ";
}

HDOJ 2647 例題 
#include <iostream>
#include <vector>
#include <stdio.h>
#include <queue>
using namespace std;
typedef pair<int,int> p;
int main(){
	int v,e,x,y;
	while(scanf("%d %d",&v,&e)!=EOF){
		vector<int> m[v+1];
		while(e--){
			scanf("%d %d",&x,&y);
			m[y].push_back(x);
		}
		int rudu[v+1];
		for(int i=0;i<v+1;i++) rudu[i]=0;
		for(int i=1;i<=v;i++) for(int j=0;j<m[i].size();j++) rudu[m[i][j]]++;
		
		queue<p> q;
		for(int i=1;i<=v;i++) if(!rudu[i]) q.push(p(i,888));
		
		int count=0,ans=0;
		while(!q.empty()){
			p t=q.front();
			q.pop();
			ans+=t.second;
			count++;
			for(int i=0;i<m[t.first].size();i++) if(--rudu[m[t.first][i]]==0) q.push(p(m[t.first][i],t.second+1));
		}
		if(count!=v) cout<<"-1"<<endl;
		else cout<<ans<<endl; 
	}
}

//單源最短路問題
//beelman_ford比爾曼—福特算法  允許負邊的存在 
struct edge{
	int from,to,cost;
};
edge es[max_e];
int d[max_v];
int v,e;
void shortest_path(int s){
	for(int i=0;i<v;i++) d[i]=inf;
	d[s]=0;
	while(true){
		bool update=true;
		for(int i=0;i<e;i++){
			edge e=es[i];
			if(d[e.from]!=inf&&d[e.to]>d[e.from]+e.cost){
				d[e.to]=d[e.from]+e.cost;
				update=false;
			}
		}
		if(update) break;
	}
}
//檢查是否存在負圈 
bool find_negative_loop(){
	memset(d,0,sizeof(d));
	for(int i=0;i<v;i++){
		for(int j=0;j<e;j++){
			edge t=es[j];
			if(d[e.from]!=inf&&d[e.to]>d[e.from]+t.cost){
				d[e.to]=d[e.from]+e.cost;
				if(i==v-1) return true;
			}
		}
	}
	return false;
}
//dijkstra迪傑斯特拉算法 不允許負邊的存在 

struct edge{
	int to,cost;
};

typedef pair<int,int> p;
int v;
vector<edge> g[max_v];
int d[max_v];

void dijkstra(int s){
	priority_queue<p,vector<p>,greater<p> > q;
	fill(d,d+v,inf);
	d[s]=0;
	q.push(p(0,s));
	while(!q.empty()){
		p t=q.top();
		q.pop();
		int v=t.second;
		if(d[v]<t.first) continue; //避免上一個點到這個點有多個路徑 
		for(int i=0;i<g[v].size();i++){
			edge e=g[v][i];
			if(d[e.to]>d[v]+e.cost){
				d[e.to]=d[v]+e.cost; 
				q.push(p(d[e.to],e.to));
			}
		}
	}
}

// Floyd-warshall 弗洛伊德算法  任意兩點間最短路
int d[max_v][max_v];
int v;
void warshall_floyd(){
	for(int i=0;i<v;i++) for(int j=0;j<v;j++) if(i==j) d[i][i]=0; else d[i][j]=INF;
	for(int k=0;k<v;k++) for(int i=0;i<v;i++) for(int j=0;j<v;j++) d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}

//以dijsktra路徑還原 


struct edge{
	int to,cost;
};

typedef pair<int,int> p;
int v,s;
vector<edge> g[max_v];
int d[max_v];
int prev[max_v];

void dijkstra(int s){
	priority_queue<p,vector<p>,greater<p> > q;
	fill(d,d+v,inf);
	fill(prev,prev+v,-1);
	d[s]=0;
	q.push(p(0,s));
	while(!q.empty()){
		p t=q.top();
		q.pop();
		int v=t.second;
		if(d[v]<t.first) continue;
		for(int i=0;i<g[v].size();i++){
			edge e=g[v][i];
			if(d[e.to]>d[v]+e.cost){
				d[e.to]=d[v]+e.cost; 
				prev[e.to]=v;
				q.push(p(d[e.to],e.to));
			}
		}
	}
}

//kruskal克魯斯卡爾算法 
#include <iostream>
#include <algorithm>
using namespace std;
#define max_v 1000
#define max_e 5000
int par[max_v];
void init(int n){
	for(int i=0;i<n;i++) par[i]=i;
}

int find(int x){
	if(par[x]==x) return x;
	return par[x]=find(par[x]);
}

void unite(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y) return;
	par[x]=y;
}

bool same(int x,int y){
	return find(x)==find(y);
}

struct edge{
	int u,v,cost;
}; 
bool comp(const edge& e1,const edge& e2){
	return e1.cost<e2.cost;
}

edge es[max_e];
int v,e;


int kruskal(){
	sort(es,es+e,comp);
	init(v); //並查集初始化 
	int res=0;
	for(int i=0;i<e;i++){
		edge e=es[i];
		if(!same(e.u,e.v)){
			unite(e.u,e.v);
			res+=e.cost;
		}
	}
	return res; 
}


int main(){
	cin>>v>>e;
	for(int i=0;i<e;i++) cin>>es[i].u>>es[i].v>>es[i].cost;
	cout<<kruskal()<<endl;
	return 0;
}

//prim算法普通實現 
const inf=(1<<30);
const max_n=1000;
int cost[max_n][max_n];
int mincost[max_n];
bool used[max_n];
int n;
int prim(){
	for(int i=0;i<n;i++){
		used[i]=false;
		mincost[i]=inf;
	}
	mincost[0]=0;
	int res=0;
	while(true){
		int v=-1;
		for(int i=0;i<n;i++) if(!used[i]&&(v==-1||mincost[i]<mincost[v])) v=i;
		if(v==-1) break;
		used[v]=true;
		res+=mincost[v];
		for(int i=0;i<n;i++) mincost[i]=min(mincost[i],cost[u][v]); 
	}
	return res;
}

//prim算法小根堆實現 

#include <iostream>  
#include <queue>  
#include <vector>  
using namespace std;  
typedef pair<int,int> p;  
  
  
const int max_n=1000;  
const int inf=(1<<30);  
vector<p> m[max_n];  
bool used[max_n];  
int v,e;  
int prim(){  
    priority_queue<p,vector<p>,greater<p> > q;  
    q.push(p(0,1));  
    int res=0,num=0;  
    while(!q.empty()){  
        p t=q.top();  
        q.pop();  
        num++;  
        res+=t.first;  
        int v=t.second;  
        used[v]=1;  
        for(int i=0;i<m[v].size();i++){  
            p temp=m[v][i];  
            if(!used[temp.second]) q.push(temp);   
        }  
        while(!q.empty()&&used[q.top().second]) q.pop();  
    }  
    if(num==v) return res;  
    else return -1;// 表示是一個非連通圖   
}  
  
  
int main(){  
    int from,to,c;  
    while(cin>>v>>e,~v){  
        for(int i=0;i<v+1;i++){  
            m[i].clear();  
            used[i]=false;  
        }  
        while(e--){  
            cin>>from>>to>>c;  
            m[from].push_back(p(c,to));  
            m[to].push_back(p(c,from));  
        }  
        cout<<prim()<<endl;  
    }  
      
    return 0;  
}  
/* 
測試範例: 
7 9  
1 2 1 
2 3 2 
2 4 3 
3 5 10 
2 7 7 
4 7 1 
4 6 5 
5 7 5 
6 7 8 
*/ 


//sort的比較函數返回值意義是:爲真時前置a 
//升序排列:
bool comp(int a,int b){ return a<b; }
//降序排列:
bool comp(int a,int b){ return a>b; } 

//針對有序數組可用的三個查找函數 
/*
lower_bound(int *start,int *end,int a) :返回值是區間中第一個大於等於a的元素的指針 
upper_bound(int *start,int *end,int a) :返回值是區間中第一個大於a的元素的指針
equal_bound(int *start,int *end,int a) :返回值是一個pair<int*,int*>,是指向相等元素所在區間的 左閉右閉 區間的一對指針

int a[7]={1,2,3,3,3,4,5};
pair<int*,int*> t=equal_range(a,a+7,3); 
for(int* i=t.first;i!=t.second;i++) cout<<*i<<" ";

注意事項:
1.默認傳入區間元素已經是升序排列
2.cout<<*lower_bound(a,a+7,3)<<endl; 這樣即可直接確定值
3.返回值直接減數組名可得下標值: lower_bound(int *start,int *end,int a)-num表示插入a時a應插入位置 
4.upper_bound(int *start,int *end,int a)-lower_bound(int *start,int *end,int a)可求得相等元素個數
5.可自己編寫第四個參數並傳入作爲比較規則 
*/

//枚舉函數

#include <iostream>
#include <algorithm>
using namespace std;

int main(){
	int a[5]={1,2,3,4,5};
	stable_sort(a,a+5);//從小到大 
	do{
		for(int i=0;i<5;i++) cout<<a[i]<<" ";
		cout<<endl;
	}while(next_permutation(a,a+5));
	stable_sort(a,a+5,greater<int>());//從大到小 
	do{
		for(int i=0;i<5;i++) cout<<a[i]<<" ";
		cout<<endl;
	}while(prev_permutation(a,a+5));
	return 0;
}  

//揹包dp

//01揹包問題 (每種物品只有一個) 
#include <iostream>
#include <stdio.h>
using namespace std;

int main(){
	int n,m;//n是物品geshu,m是揹包最大容量
	scanf("%d",&n);
	int w[n],v[n];// w是物品費用,v是物品價值
	for(int i=0;i<n;i++) scanf("%d%d",&w[i],&v[i]);
	int dp[m+1];// 空間壓縮的dp數組
	for(int i=0;i<=m;i++) dp[i]=0; //要求裝滿揹包時初始化爲inf 
	for(int i=0;i<n;i++) for(int j=m;j>=w[i];j--) dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	printf("%d",dp[m]); //能獲得的最大價值 
	return 0;
}

//完全揹包問題  (每種物品無限個)
#include <iostream>
#include <stdio.h>
using namespace std;

int main(){
	int n,m;//n是物品geshu,m是揹包最大容量
	scanf("%d",&n);
	int w[n],v[n];// w是物品費用,v是物品價值
	for(int i=0;i<n;i++) scanf("%d%d",&w[i],&v[i]);
	int dp[m+1];// 空間壓縮的dp數組
	for(int i=0;i<=m;i++) dp[i]=0;
	for(int i=0;i<n;i++) for(int j=w[i];j<=m;j++) dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	printf("%d",dp[m]); //能獲得的最大價值 
	return 0;
}

//多重揹包問題 (每種物品有ki個)

#include <iostream>
#include <stdio.h>
using namespace std;

int main(){
	int n,m;//n是物品geshu,m是揹包最大容量
	scanf("%d",&n);
	int w[n],v[n],k[n];// w是物品費用,v是物品價值,k是物品個數 
	for(int i=0;i<n;i++) scanf("%d%d%d",&w[i],&v[i],&k[i]);
	int dp[m+1];// 空間壓縮的dp數組
	for(int i=0;i<=m;i++) dp[i]=0;
	for(int i=0;i<n;i++){
		if(k[i]*w[i]>=m) for(int j=w[i];j<=m;j++) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 
		else{
			int tk=k[i];
			int k=1;
			while(k<=tk){
				for(int j=m;j>=k*w[i];j--) dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]);
				tk-=k;
				k<<=1;
			}
			if(tk) for(int j=m;j>=tk*w[i];j--) dp[j]=max(dp[j],dp[j-tk*w[i]]+tk*v[i]);
		}
	}
	printf("%d",dp[m]); //能獲得的最大價值 
	return 0;
}

//3種揹包問題的封裝

void zeroonepack(int w,int v){    //01揹包 
	for(int i=m;i>=w;i--) dp[i]=max(dp[i],dp[i-w]+v);
}

void completepack(int w,int v){   //完全揹包問題 
	for(int i=w;i<=m;i++) dp[i]=max(dp[i],dp[i-w]+v);
}

void multiplepack(int w,int v,int k){
	if(w*k>=m) completepack(w,v);
	else{
		for(int t=1;t<=k;k-=t,t<<=1) zeroonepack(t*w,t*v);
		if(k) zeroonepack(k*w,k*v);
	}
}

//LCS 最長公共子序列 待搞成m+n

#include <iostream>
using namespace std;

int main(){
	string a,b;
	cin>>a>>b;
	int alen=a.length(),blen=b.length();
	int dp[alen+1][blen+1];
	for(int i=0;i<alen;i++) dp[i][0]=0;
	for(int i=0;i<blen;i++) dp[0][i]=0;
	for(int i=0;i<alen;i++){
		for(int j=0;j<blen;j++){
			if(a[i]==b[j]) dp[i+1][j+1]=dp[i][j]+1;
			else dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
		}
	}
	cout<<dp[alen][blen]<<endl; //a和b的最長公共子序列長度
	int i=alen,j=blen,k=0;  //輸出最長公共子序列 
	int path[alen];
	while(dp[i][j]){  
	    if(dp[i][j]==dp[i-1][j]) i--;  
	    else if(dp[i][j]==dp[i][j-1]) j--;  
	    else{ 
	    	path[k++]=i-1;
	        i--;
			j--;  
	    }  
	}
	for(int i=k-1;i>0;i--) printf("%c ",a[path[i]]);
	printf("%c\n",a[path[0]]);
	return 0;
}

//LIS最長上升子序列  n*logn複雜度 二分法實現 
#include <iostream>
#include <stdio.h>
using namespace std;
const int inf=(1<<30);
int main(){
	int n,t,res=0;
	scanf("%d",&n);
	int a[n],dp[n];
//dp[i]的含義是長度爲i+1的上升子序列中末尾元素的最小值,故dp數組中存的不是最長上升子序列 
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	fill(dp,dp+n,inf);
	for(int i=0;i<n;i++) *lower_bound(dp,dp+n,a[i])=a[i];
	printf("%d\n",lower_bound(dp,dp+n,inf)-dp);
	return 0;
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章