数据结构
链表与邻链表
struct Node
{
int val;
Node *next
}
new Node(); //非常慢
数组模拟单链表 静态链表
int head; //头节点
int e[N]; //值
int ne[N]; //next指针
int idx; //数组用到第几个点
// 初始化
void init()
{
head = -1;
idx = 0;
}
// 在链表头插入一个数a
void insert_head(int a)
{
e[idx] = a;
ne[idx] = head;
head = idx;
idx++ ;
}
// 将头结点删除,需要保证头结点存在
void remove()
{
head = ne[head];
}
// 将a插入到第k个点后面
void add(int k, int a)
{
e[idx] = a;
ne[idx] = ne[k];
ne[k] = idx;
idx ++;
}
//将下标是k的后边的点删掉
void remove(int k)
{
ne[k] = ne[ne[k]];
}
数组模拟双链表(优化某些问题)
每一个点有两个指针
int e[N];
int l[N];
int r[N];
//初始化
void init()
{
// 0表示左端点(head), 1表示右端点(tail)
r[0] = 1;
l[1] = 0;
idx = 2;
}
//在下标是k的点的右边,插入x
void add(int k, int x)
{
e[idx] = x;
r[idx] = r[k]; //不能用 k + 1
l[idx] = k;
l[r[k]] = idx; //不能用 k + 1
r[k] = idx;
}
//在k的左边插入x
add(l[k], x);
//删除第k个点
void remove(int k)
{
r[l[k]] = r[k];
l[r[k]] = l[k];
}
邻接表 存储树和图
栈与队列(吐出来的是栈,拉出来的是队列)
数组模拟栈(先进后出)
int stk[N], tt;
//插入
stk[++ tt] = x;
//弹出
tt --;
//判断栈是否为空
if(tt > 0) 不空
//栈顶
skt[tt];
数组模拟队列(先进先出)
//队尾插入,队头弹出
int q[N];
int hh; //头
int tt = -1; //尾
//插入
q[++tt] = x
//弹出
hh++;
//判断队列是否为空
if(hh <= tt) 不空
//取出队头元素
q[hh]
单调栈和单调队列(优化暴力,去掉中间没有用的元素,剩下元素有单调性可以做优化)
单调栈
eg:(左\右边有没有大\小的数) 变成严格单调上升的栈
单调队列
eg:求滑动窗口的最大值\最小值
kmp算法(克努特-莫里斯-普拉特操作)
- kmp整体思路
问题:在文本串中找子串
例子:在aabaabaaf中找aabaaf
遍历子串得到next数组(最大相等前后缀长度)
文本串中寻找子串存在的位置
根据next数组更新位置
next数组情况分析:
0 | 1 | 0 | 1 | 2 | 0 | 最大相等前后缀长度 | j = next[j - 1] |
---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 0 | 1 | 2 | 右移一位 | j = next[j] |
-1 | 0 | -1 | 0 | 1 | -1 | 每个位置-1 | j = next[j] + 1 |
void getNext(vector<int>& next, string x){
//i:后缀尾位置,j:前缀尾位置(也是长度)
int j = 0;
//初始化next数组
next.push_back(0);
for(int i = 1; i < x.length(); i++){
while(j > 0 && x[i] != x[j] ) j = next[j - 1]; //处理不同情况
if(x[i] == x[j]) j++; //处理相同情况
next.push_back(j); //更新next数组
}
}
int strStr(string text, string son) {
//i:文本串位置,j:子串位置
vector<int> next;
getNext(next, son);
int j = 0;
for(int i = 0; i < text.size(); i++){
while(j > 0 && text[i] != sonson[j]) j = next[j - 1]; //处理不同情况
if(text[i] ==son[j]) j++; //处理相同情况
... //根据题目要求操作
}
return -1;
}
Trie树(字典树)
- 快速存储和查找字符串集合的数据结构
- 按照每个字符串顺序进行建树,标记字符串结尾的地方/标记出现次数
字符串集合是{in, inn, int, tea, ten, to}
//每个点存的是数字
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];
}
并查集(近乎O(1))
- 功能:
- 将两个集合合并
- 询问两个元素是否在一个集合当中
- 基本原理
- 每个集合用一颗树来表示,树根编号就是整个集合的编号,每个节点存储他的父节点(p[x])
- 操作
- 问题1:如何判断树根:if(p[x] == x)
- 问题2:如何求x的集合编号:while(p[x] != x) x = p[x]
- 问题3:如何合并两个集合,p[x]是x的集合编号,p[y]是y的集合编号,p[x] = y
- 优化
- 查一次之后直接指向祖先(路径压缩)
路径压缩:
- 统计一个集合中数的数量(size[i])
- 合并集合:if(b的根节点 != a的根节点) size[b的根节点] += size[a的根节点]
(1)朴素并查集:
int p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
(2)统计每个集合有多少个元素的并查集:
int p[N], size[N];
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
size[i] = 1;
}
// 合并a和b所在的两个集合:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);
堆(是一个完全二叉树:除最后一层,都是满的,最后一层从左向右排列)
eg: 小根堆,每一个点小于左右儿子
- 用一维数组存储, 左儿子2x, 右儿子2x+1
- 下标从1开始
操作 | 代码 |
---|---|
插入一个数 | 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] |
int h[N], size;
//down
//把一个值变大时候down
//跟左右节点的最小值交换
void down(int u)
{
int t = u;
if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2;
if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
if (u != t) //最小值的下标不同的话
{
swap(h[u], h[t]);
down(t);
}
}
//up
//把一个值变小时候up
//跟父节点交换,因为小根堆
void up(int u)
{
while (u / 2 && h[u] < h[u / 2])
{
swap(h[u], h[u / 2]);
u >>= 1;
}
}
// O(n)建堆
for (int i = n / 2; i; i -- ) down(i);
哈希表
存储结构
开放寻址法
拉链法
字符串哈希
哈希函数 10^9 - 10^9 映射到0 - 10^5
x mod 10^5
会发生冲突,处理冲突
- 拉链法:
添加,查找
- 开放寻址法
待学习...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。