代码模板

单链表

/ head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点
int head, e[N], ne[N], idx;

// 初始化
void init()
{
	head = -1;
	idx = 0;
}

// 在链表头插入一个数a
void insert(int a)
{
	e[idx] = a, ne[idx] = head, head = idx ++ ;
}

// 将头结点删除,需要保证头结点存在
void remove()
{
	head = ne[head];
}

双链表

// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;

// 初始化
void init()
{
	//0是左端点,1是右端点
	r[0] = 1, l[1] = 0;
	idx = 2;
}

// 在节点a的右边插入一个数x
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];
}

// tt表示栈顶
int stk[N], tt = 0;

// 向栈顶插入一个数
stk[ ++ tt] = x;

// 从栈顶弹出一个数
tt -- ;

// 栈顶的值
stk[tt];

// 判断栈是否为空
if (tt > 0)
{

}

队列

// hh 表示队头,tt表示队尾
int q[N], hh = 0, tt = -1;

// 向队尾插入一个数
q[ ++ tt] = x;

// 从队头弹出一个数
hh ++ ;

// 队头的值
q[hh];

// 判断队列是否为空
if (hh <= tt)
{

}

循环队列

// hh 表示队头,tt表示队尾的后一个位置
int q[N], hh = 0, tt = 0;

// 向队尾插入一个数
q[tt ++ ] = x;
if (tt == N) tt = 0;

// 从队头弹出一个数
hh ++ ;
if (hh == N) hh = 0;

// 队头的值
q[hh];

// 判断队列是否为空
if (hh != tt)
{

}

单调栈

常见模型:找出每个数左边离它最近的比它大/小的数
int tt = 0;
for (int i = 1; i <= n; i ++ )
{
	while (tt && check(stk[tt], i)) tt -- ;
	stk[ ++ tt] = i;
}

单调队列

常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
	while (hh <= tt && check_out(q[hh])) hh ++ ;  // 判断队头是否滑出窗口
	while (hh <= tt && check(q[tt], i)) tt -- ;
	q[ ++ tt] = i;
}

KMP

// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
    while (j && p[i] != p[j + 1]) j = ne[j];
    if (p[i] == p[j + 1]) j ++ ;
    ne[i] = j;
}

// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
    while (j && s[i] != p[j + 1]) j = ne[j];
    if (s[i] == p[j + 1]) j ++ ;
    if (j == m)
    {
        j = ne[j];
        // 匹配成功后的逻辑
    }
}


Tire树

int son[N][26], cnt[N], idx;
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点
// cnt[]存储以每个节点结尾的单词数量

// 插入一个字符串
void insert(char *str)
{
	int p = 0;
	for (int i = 0; str[i]; i ++ )
	{
		int u = str[i] - 'a';
		if (!son[p][u]) son[p][u] = ++ idx;
		p = son[p][u];
	}
	cnt[p] ++ ;
}

// 查询字符串出现的次数
int query(char *str)
{
	int p = 0;
	for (int i = 0; str[i]; i ++ )
	{
		int u = str[i] - 'a';
		if (!son[p][u]) return 0;
		p = son[p][u];
	}
	return cnt[p];
}

并查集

int findFather(int x) {
	int a=x;
	while(x!=father[x]) {
		x=father[x];
	}
	while(a!=father[a]) {//路径压缩
		int z=a;
		a=father[a];
		father[z]=x;
	}
	return x;
}

void Union(int a,int b) {//合并
	int faA=findFather(a);
	int faB=findFather(b);
	if(faA!=faB) father[faA]=faB;
}

哈希

(1) 拉链法
    int h[N], e[N], ne[N], idx;

    // 向哈希表中插入一个数
    void insert(int x)
    {
        int k = (x % N + N) % N;
        e[idx] = x;
        ne[idx] = h[k];
        h[k] = idx ++ ;
    }

    // 在哈希表中查询某个数是否存在
    bool find(int x)
    {
        int k = (x % N + N) % N;
        for (int i = h[k]; i != -1; i = ne[i])
            if (e[i] == x)
                return true;

        return false;
    }

(2) 开放寻址法
    int h[N];

    // 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
    int find(int x)
    {
        int t = (x % N + N) % N;
        while (h[t] != null && h[t] != x)
        {
            t ++ ;
            if (t == N) t = 0;
        }
        return t;
    }

字符串哈希

核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果

typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64

// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
	h[i] = h[i - 1] * P + str[i];
	p[i] = p[i - 1] * P;
}

// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
	return h[r] - h[l - 1] * p[r - l + 1];
}

//向下调整
void downAdjust(int low,int high) {
	int i=low,j=i*2;
	while(j <= high) {
		if(j+1<=high && heap[j+1]>heap[j]) {
			j=j+1;
		}
		if(heap[j]>heap[i]) {
			swap(heap[j],heap[i]);
			i=j;
			j=i*2;
		} else {
			break;
		}
	}
}
//构建大顶堆
void createHeap() {
	for(int i=n/2; i>=1; i--) {
		downAdjust(i,n);
	}
}
//向上调整
void upAdjust(int low,int high) {
	int i=high,j=i/2;
	while(j>=low) {
		if(heap[j]<heap[i]) {
			swap(heap[j],heap[i]);
			i=j;
			j=i/2;
		} else {
			break;
		}
	}
}
//向大顶堆中插入数据x
void insert(int x) {
	heap[++n]=x;
	upAdjust(1,n);
}
//堆排序
void heapSort() {
	createHeap();
	for(int i=n; i>1; i--) {
		swap(heap[i],heap[1]);
		downAdjust(1,i-1);
	}
}

LCA

node* LCA(node *root,node *p,node *q) {
	if(root==NULL) return NULL;
	if(root->data==p->data||root->data==q->data) return root;
	node *left=LCA(root->lchild,p,q);
	node *right=LCA(root->rchild,p,q);
	if(left!=NULL&&right!=NULL) return root;
	return left==NULL?right:left;//p q 分别位于左右子树的情况
}

打表 埃式筛法

int prime[maxn],pNum=0;
bool p[maxn]= {0}
void Find_Prime() {
	fot(int i=2; i<maxn; i++) {
		if(p[i]==false) {
			prime[pNum++]=i;
			for(int j=i*i; j<maxn; j+=i) {
				p[j]=true;
			}
		}
	}
}

冒泡

void Bubble_sort(int s[],int len) {
	bool flag;
	for(int i=1; i<len-1; ++i) {
		flag=false;
		for(int j=1; j<=len-i; ++j)
			if(s[j]>s[j+1]) {
				flag=true;
				swap(s[j],s[j+1]);
			}
		if(!flag)break;
	}
}

选择

void Select_sort(int s[],int len) {
	int k;
	for(int i=1; i<len; ++i) {
		k=i;
		for(int j=i+1; j<=len; ++j)
			if(s[k]>s[j])k=j;
		if(k!=i)swap(s[i],s[k]);

	}

}

快排

int Quick_sort(int s[],int low,int high) {
	int tmp=s[low];//首元素是枢轴
	while(low<high) { //待排序序列长度大于1
		while(low<high&&tmp<=s[high])--high;//大于枢轴的元素依旧在右边
		s[low]=s[high];//将小于枢轴的元素放左边
		while(low<high&&tmp>=s[low])++low;//小于枢轴的元素依旧在左边
		s[high]=s[low];//将大于枢轴的元素放右边
	}
	s[low]=tmp;//枢轴记录到位
	return low;//返回枢轴的位置,表示该位置已经排好序
}
void Qsort(int s[],int low,int high) {
	if(low<high) {
		int key=Quick_sort(s,low,high); // 第key个元素已经排好序,继续两边搜索排序
		Qsort(s,low,key-1);
		Qsort(s,key+1,high);
	}
}

归并

//非递归

void mergeSort() {//这道题戏剧性地不能用归并递归
	for(int step=2; step/2<=n; step*=2) {
		for(int i=1; i<=n; i+=step) {
			int mid=i+step/2-1;
			if(mid+1<=n) {//右边区间存在
				merge(i,mid,mid+1,min(i+step-1,n));//这个函数不写了
			}
		}
	}
}

//递归版本自己看一下

关键路径

应用举例:有向无环图的最长路径

#include <iostream>
#include <stack>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
const int MAXV=100;
vector<int> G[MAXV];
stack<int> topOrder;
int inDegree[MAXV];
bool topologicalSort() {
	queue<int> q;
	for(int i=0; i<n; i++) {
		if(inDegree[i]==0) {
			q.push(i);
		}
	}
	while(!q.empty()) {
		int u=q.front();
		q.pop();
		topOrder.push(u);
		for(int v=0; v<G[u].size; v++) {
			int vex=G[u][v].v;
			inDegree[vex]--;
			if(inDegree[v]==0) {
				q.push(v);
			}
			if(ve[u]+G[u][v].w>ve[vex]) {//计算ve[...]
				ve[v]=ve[u]+G[u][v];
			}
		}
	}
	if(topOrder.size()==n) return true;
	else return false;
}
int CriticalPath(bool falg) {//求关键路径长度
	memset(ve,0,sizeof(ve));//ve初始化
	if(topologicalSort()==false) {
		return -1;//不是有向无环图
	}
	fill(vl,vl+n,ve[n-1]);//vl数组初始化,如果不确定汇点,就在ve中求出最大值来代替ve[n-1]
	while(!topOrder.empty()) {
		int u=topOrder.top();
		topOrder.pop();
		for(int i=0; i<G[u].size(); i++) {
			int v=G[u][i].v;
			if(vl[v]-G[u][i].w<vl[u]) {
				vl[u]=vl[v]-G[u][i].w;
			}
		}
	}
	//遍历领接表所有边,计算获得最早时间和最迟时间
	for(int u=0; u<n; u++) {
		for(int i=0; i<G[u].size(); i++) {
			int v=G[u][i].v,w=G[u][i].w;
			int e=ve[u],l=vl[v]-w;
			if(e==l) {
				printf("%d->%d\n",u,v);//输出关键活动
			}
		}
	}
	return ve[n-1];//返回关键路径长度
}
int main() {
	int len=CriticalPath(flag);
	return 0;
}

最小生成树

// 邻接矩阵实现
int G[maxn][maxn];// 邻接矩阵
int vis[maxn];// 访问矩阵
int d[maxn];// 顶点与集合 S 的最短距离
int n;
int Prim() {// 默认 0 号为初始点,函数返回最小生成树的边权之和
	int i,j;
	int ans,u,v,min;
	for(i=0; i<n; i++) {
		d[i]=INF;
	}
	d[0]=0;// 初始化 d[] ,除 0 号为 0 外其余全为 INF
	ans=0;// 存放最小生成树边权之和
	for(i=0; i<n; i++) {
		u=-1,min=INF;
		for(j=0; j<n; j++) {
			if(vis[j]==0&&d[j]<min) {
				u=j;
				min=d[j];
			}
		}
// 找不到小于 INF 的 的 d[u], 则剩下的顶点和集合 S 不连通
		if(u=-1) {
			return -1;
		}
		vis[u]=1;// 设置 u 为已访问
		ans+=d[u]// 加入最小生成树
		for(v=0; v<n; v++) {
//v 未访问&&u 能到达 v&&以 以 u 为中介点可以使 v 离集合 S 更近
			if(vis[v]==0&&G[u][v]!=INF&&G[u][v]<d[v]) {
				d[v]=G[u][v];//将 将 G[u][v] 赋值给 d[v]
			}
		}
	}
	return ans;// 返回边权之和

哈夫曼

只给一个例子求最小权重,多个苹果不同重量,怎么提到目的地花力气最少。

#include <iostream>
#include <queue>
using namespace std;
priority_queue<int,vector<int>,greater<int>> q;//构建小顶堆

int main() {
	int n,weight,ans=0;//ans计算最后总权重
	cin>>n;
	for(int i=0; i<n; i++) {
		cin>>weight;
		q.push(weight);
	}
	while(q.size()>1) {//当优先队列内元素最少有2个时候才开始合并
		int x=q.top();
		q.pop();
		int y=q.top();
		q.pop();
		q.push(x+y);
		ans+=x+y;
	}
	cout<<ans;
	return 0;
}

剩余补充

进制转换代码模板:
int z[50],num=0;
	do{
	//来做进制的转换
		z[num++]=n%b;
		n=n/b;
	}while(n!=0);

转换为b进制后转换回来为十进制的代码模板
for(int i=num-1;i≥0;i--){
		n=n*b+z[i];
}



求最大最小公约数代码模板:求最大公约数
int gcd(int a,int b){
   return !b ? a: gcd(b,a%b)
}

最小公倍数就是求出来的最大公倍数m后让a*b/m
尽量用long long类型
posted @ 2021-02-17 16:39  coderJ_ONE  阅读(105)  评论(0)    收藏  举报