一、单链表
#include <iostream>
using namespace std;
const int N=100010;
int head,e[N],ne[N],idx;
int init()
{
head = -1;
idx = 0;
}
//将x插到head结点的后边
void add_to_head(int x)
{
e[idx] = x;
ne[idx] = head;
head = idx;
idx++;
}
//将x插到下标是k的结点的后边
void add(int k,int x)
{
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx;
idx++;
}
//将下标是k的后边的结点删掉
void rmv(int k)
{
ne[k] = ne[ne[k]];
}
int main()
{
int m;
cin >> m;
init();
while(m--)
{
int k,x;
char op;
cin >> op;
if(op == 'H')
{
cin >> x;
add_to_head(x);
}
else if(op == 'D')
{
cin >> k;
if(!k)
head = ne[head];
rmv(k-1);
}
else
{
cin >> k >> x;
add(k-1,x);
}
}
for(int i=head;i!=-1;i=ne[i])
cout << e[i] << " ";
cout << endl;
return 0;
}
二、双链表
int init()
{
r[0] = 1;
l[1] = 0;
idx = 2;
}
//将x插到下标是k的结点的右边
void add(int k,int x)
{
e[idx] = x;
l[idx] = k;
r[idx] = r[k];
l[r[k]] = idx;
r[k] = idx;
}
//将下标是k的结点删掉
void rmv(int k)
{
r[l[k]] = r[k];
l[r[k]] = l[k];
}
#include <iostream>
using namespace std;
const int N = 10010;
int m;
int e[N],l[N],r[N],idx;
void init()
{
r[0] = 1;
l[1] = 0;
idx = 2;
}
//在第k个插入的结点后面插入新的结点
void add(int k,int x)
{
e[idx] = x;
r[idx] = r[k];
l[idx] = k;
l[r[k]] = idx;
r[k] = idx;
idx++;
}
//删除第k个插入的结点
void remove_k(int k)
{
r[l[k]] = r[k];
l[r[k]] = l[k];
}
/*
L x,表示在链表的最左端插入数 x。
R x,表示在链表的最右端插入数 x。
D k,表示将第 k 个插入的数删除。
IL k x,表示在第 k 个插入的数左侧插入一个数。
IR k x,表示在第 k 个插入的数右侧插入一个数。
*/
int main()
{
cin >> m;
init();
while(m--)
{
char c;
int x,k;
cin >> c;
switch(c)
{
case 'L':
cin >> x;
add(0,x);
break;
case 'R':
cin >> x;
add(l[1],x);
break;
case 'D':
cin >> k;
remove_k(k+1);
break;
case 'I':
char cc;
cin >> cc;
cin >> k >> x;
if(cc == 'L')
add(l[k+1],x);
else
add(k+1,x);
break;
}
}
for(int i=r[0];i!=1;i=r[i])
cout << e[i] << " ";
return 0;
}
三、栈
从0开始
int stk[N],tt=0;
//插入
stk[++tt] = x;
//弹出
tt--;
//判断栈是否为空
if(tt>0)
return false;
else
return true;
//栈顶
stk[tt];
四、 队列
从-1开始
int q[N],hh,tt=-1;
//插入
q[++tt] = x;
//弹出
hh++;
//判断队列是否为空
if(hh<=tt)
return false;
else
return true;
//取队头,队尾
q[hh],q[tt];
五、单调栈
例:左边离最近的比它小的数
#include <iostream>
#include <stdio.h>
using namespace std;
const int N = 100010;
int n;
int stk[N],tt;
int main()
{
cin >> n;
for(int i=0;i<n;i++)
{
int x;
scanf("%d",&x);
while(tt && stk[tt]>=x)
tt--;
if(tt)
printf("%d ",stk[tt]);
else
printf("-1 ");
stk[++tt] = x;
}
return 0;
}
六、 单调队列
滑动窗口最大值,最小值
#include <iostream>
#include <stdio.h>
using namespace std;
const int N = 100010;
int n,k;
int a[N],q[N];
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
//最小值
int hh = 0,tt = -1;
for(int i=0;i<n;i++)
{
//判断对头是否已经滑出窗口
if(hh<=tt && i-k+1>q[hh])
hh++;
while(hh<=tt && a[q[tt]]>=a[i])
tt--;
q[++tt] = i;
if(i>=k-1)
printf("%d ",a[q[hh]]);
}
puts("");
//最大值
hh = 0,tt = -1;
for(int i=0;i<n;i++)
{
//判断对头是否已经滑出窗口
if(hh<=tt && i-k+1>q[hh])
hh++;
while(hh<=tt && a[q[tt]]<=a[i])
tt--;
q[++tt] = i;
if(i>=k-1)
printf("%d ",a[q[hh]]);
}
puts("");
return 0;
}
七、 KMP
最大的前缀=后缀的长度
#include <iostream>
#include <stdio.h>
using namespace std;
const int N = 100010,M = 100010;
int n,m;
char p[N],s[M];
int ne[N];
int main()
{
cin >> n >> p+1 >> m >> s+1;
//求next数组,j代表前缀的长度
for(int i=2,j=0;i<=n;i++)
{
while(j && p[i]!=p[j+1])
j = ne[j];
if(p[i] == p[j+1])
j++;
ne[i] = j;
}
//kmp匹配过程
for(int i=1,j=0;i<=m;i++)
{
while(j && s[i]!=p[j+1])
j = ne[j];
if(s[i] == p[j+1])
j++;
if(j == n)
{
printf("%d ",i-n);
j = ne[j];
}
}
return 0;
}
八、 Trie树
高效地存储和查找字符字符串集合的数据结构
#include <iostream>
#include <stdio.h>
using namespace std;
const int N = 100010;
int son[N][26],cnt[N],idx;
char str[N];
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 main()
{
int n;
scanf("%d",&n);
while(n--)
{
char op[2];
scanf("%s%s",op,str);
if(op[0] == 'I')
Insert(str);
else
printf("%d\n",Query(str));
}
return 0;
}
九、 并查集
快速将两个集合合并;询问两个元素是否在一个集合当中。
#include <iostream>
#include <stdio.h>
using namespace std;
const int N = 100010;
int fa[N];
void init(int n)
{
for(int i=0;i<n;i++)
fa[i] = i;
}
int findfa(int x)
{
if(x != fa[x])
fa[x] = findfa(fa[x]);
return fa[x];
}
void Union(int a,int b)
{
int faA = findfa(a);
int faB = findfa(b);
if(faA != faB)
fa[faB] = faA;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
init(n);
while(m--)
{
char op[2];
int a,b;
scanf("%s%d%d",op,&a,&b);
if(op[0] == 'M')
Union(a,b);
else
{
if(findfa(a) == findfa(b))
printf("Yes");
else
printf("No");
}
}
return 0;
}
连通块中点的数量
#include <iostream>
#include <stdio.h>
using namespace std;
const int N = 100010;
int fa[N],cnt[N];
void init(int n)
{
for(int i=0;i<n;i++)
{
fa[i] = i;
cnt[i] = 1;
}
}
int findfa(int x)
{
if(x != fa[x])
fa[x] = findfa(fa[x]);
return fa[x];
}
void Union(int a,int b)
{
int faA = findfa(a);
int faB = findfa(b);
if(faA != faB)
{
fa[faB] = faA;
cnt[faA] += cnt[faB];
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
init(n);
while(m--)
{
char op[3];
int a,b;
scanf("%s",op);
if(op[0] == 'C')
{
scanf("%d%d",&a,&b);
Union(a,b);
}
else if(op[1] == '1')
{
scanf("%d%d",&a,&b);
if(findfa(a) == findfa(b))
printf("Yes");
else
printf("No");
}
else
{
scanf("%d",&a);
printf("%d\n",cnt[findfa(a)]);
}
}
return 0;
}
十、堆排序
堆是一个完全二叉树。小根堆每个点都是小于等于左右儿子的。
操作:
操作 | 代码 |
---|---|
插入一个数 | heap[++size]=x;up(size) |
求集合中的最小值 | heap[1] |
删除最小值 | heap[1]=heap[size];size–;down(1) |
删除任意一个元素 | heap[k]=heap[size];size–;down(k);up(k) |
修改任意一个元素 | heap[k]=x;down(k);up(k); |
存储:
- 1号点是根结点。
- x的左儿子是2x;x的右儿子是2x+1。
down操作:
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int N = 100010;
int h[N],cnt;
int n,m;
void down(int u)
{
int t = u;
if(2*u<=cnt && h[2*u]<h[t])
t = 2*u;
if(2*u+1<=cnt && h[2*u+1]<h[t])
t = 2*u+1;
if(u != t)
{
swap(h[u],h[t]);
down(t);
}
}
void up(int u)
{
while(u/2 && h[u/2]>h[u])
{
swap(h[u],h[u/2]);
u /= 2;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]);
cnt = n;
for(int i=n/2;i>0;i--)
down(i);
while(m--)
{
printf("%d ",h[1]);
h[1] = h[cnt];
down(1);
}
return 0;
}
十一、哈希表
处理冲突的常用方法:拉链法,开放寻址法。
拉链法:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int N = 100003;//大于范围的素数
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=h[i])
if(e[i] == k)
return true;
return false;
}
int main()
{
int n;
scanf("%d",&n);
memset(h,-1,sizeof(h));
while(n--)
{
char op[2];
int x;
scanf("%s%d",op,&x);
if(op[0] == 'I')
Insert(x);
else if(Find(x))
printf("Yes");
else
printf("No");
}
return 0;
}
开放寻址法:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200003;
int h[N];
int Find(int x)
{
int k = (x%N+N)%N;
while(h[k]!=INF && h[k]!=x)
{
k++;
if(k == N)
k = 0;
}
return k;
}
int main()
{
int n;
scanf("%d",&n);
memset(h,0x3f,sizeof(h));
while(n--)
{
char op[2];
int x;
scanf("%s%d",op,&x);
int k = Find(x);
if(op[0] == 'I')
h[k] = x;
else if(h[k]!=INF)
printf("Yes");
else
printf("No");
}
return 0;
}
十二、STL
vector:变长数组,倍增的思想
pair:存储二元组
string:substr(),c_str()
queue:队列,push(),front(),pop()
priority_queue,优先队列,push(),top(),pop()
stack
deque
set,map,multiset,multimap,基于平衡二叉树(红黑树),动态维护有序序列
unordered_set,unordered_map,unordered_multiset,unordered_multimap,哈希表
bitset,压位
//vector
//支持比较运算,按字典序排 a<b
//定义一个大小为10,值为3的数组
#include <vector>
vector<int> a(10,3);
a.size();
a.empty();
a.clear();
a.front(),a.back();
a.push_back(),a.pop_back();
a.begin(),a.end();
//pair
//支持比较运算,按字典序排先排first,在排second。
#include <utility>或
#include <map>
pair<int,string> p;
p = make_pair(10,"abtgu");
p = {20,"abtgu"};
p.first,p.second;
//string
#include <string>
string a = "abtgu";
a += "yyds";
a.size(),length();
cout << a.substr(1,2) << endl;//返回下标从1开始的2个字符。
a.c_str();//返回字符数组的起始地址
//queue
#include <queue>
queue<int> q;
q.size();
q.empty();
q = queue<int> ();
q.push(),q.pop();
q.front(),q.back();
//priority_queue,默认是大根堆
#include <queue>
priority_queue<int> h;
h.push(),h.pop();
h.top();
//stack
#include <stack>
stack<int> s;
s.size();
s.empty();
s = stack<int> ();
s.push(),s.pop();
s.top();
//deque
#include <deque>
deque<int> d;
d.size();
d.empty();
d.clear();
d.front(),d.back();
d.push_back(),d.pop_back()
d.push_front(),d.pop_front();
d.begin(),d.end();
//set,multiset
#include <set>
//set中不能有重复元素
set<int> s;
s.begin(),s.end();//++ ,--
s.insert();//插入一个数
s.find();//查找一个数,如果不存在,返回end迭代器
s.count();//返回某一个数的个数
s.erase();//输入是一个数x,删除所有x;输入是一个迭代器,删除这个迭代器
s.lower_bound(x);//返回大于等于x的最小的数的迭代器
s.upper_bound(x);//返回大于x的最小的数的迭代器
//map,multimap
#include <map>
map<string,int> m;
m["abtgu"] = 1;
cout << m["abtgu"] << endl;
m.insert();/插入一个数(pair)
m.erase();
m.find();
m.lower_bound(),m.upper_bound();
m.begin(),m.end();
//bitset
#include <bitset>
bitset<1000> s;
~,&,|,^;
>>,<<;
==,!=
count(); //返回有多少个1;
any(); //判断是否至少有一个1;
none();//判断是否全为0;
set();//把所有位置为1;
set(k,v);//将第k位变成v;
reset();//把所有位置为0;
flip();//等价于~;
flip(k);//把第k位取反