来源:https://www.acwing.com/blog/content/4019/
模拟链表
题目:Acwing
int e[N], ne[N]; // 链表元素及下个结点的地址
int head; // 头结点地址
int idx; // 可用位置
/** 创建含头结点的单链表 */
void init() {
head = 0;
// 头结点
e[0] = 0; // 值为链表长度
ne[0] = -1;
idx = 1; // 第1个结点的下标从1开始
}
/** 向链表头部插入一个数 */
void insert_head(int x) {
e[idx] = x;
ne[idx] = ne[head];
ne[head] = idx;
idx++;
e[0]++; // 链表长度+1
}
/** 删除下标为k后面的数 */
void rem(int k) {
ne[k] = ne[ne[k]];
e[0]--; // 链表长度-1
}
/** 在下标为k的位置后插入一个数 */
void insert(int k, int x) {
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx;
idx++;
e[0]++; // 链表长度+1
}
/** 遍历链表 */
void print() {
for (int i = ne[head]; i != -1; i = ne[i]) cout << e[i] << " ";
}
采用含头结点的单链表,头结点存储链表长度
元素从下标1开始存储
单调栈
题目:洛谷
注意:洛谷那个题有两个坑。1是数组要开到足够大空间,2是注意考虑相等的情况。
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
const int N = 10000010;
int a[N];
int main() {
int n;
cin >> n;
stack<int>s;
vector<int>re;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = n; i >= 1; i--) {
while (!s.empty() && a[i] >=a[s.top()])s.pop();
re.push_back(!s.empty() ? s.top() : 0);
s.push(i);
}
for (int i = n-1; i >= 0; i--) {
printf("%d ", re[i]);
}
return 0;
}
KMP
题目:洛谷
注意:Kmp算法都是从下标1开始,所以注意初始化字符串s=" "+s,但是字符串的长度还是原来的长度。另:第一步:求解匹配串的next数组。第二步:字符串匹配。
// 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]; // 匹配成功后的逻辑 ex:len =i-m+1
}
}
字典树
题目:洛谷
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 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);
// 判断两个结点是否属于同一集合
if (find(a) == find(b)) {...}
维护size
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所在的两个集合:
int x = find(a), y = find(b);
if (x != y) {
p[x] = y;
size[y] += size[x];
}
// 判断两个结点是否属于同一集合
if (find(a) == find(b)) {...}
模拟堆
题目:洛谷,力扣
// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1
int h[N], size;}
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(u, t);
down(t);
}
}
void up(int u)
{
while (u / 2 && h[u] < h[u / 2])
{
swap(u, u / 2);
u >>= 1;
}
}
// --------------------------------基本操作--------------------------------
// 0. 建堆
void init() {
for (int i = n / 2; i; i -- ) down(i);
}
// 1. 插入一个数
void insert(int x) {
h[++size] = x;
up[size];
}
// 2. 求最小值
int top() {
return h[1];
}
// 3. 删除最小值
void remove() {
h[1] = h[size];
size--;
down(1);
}
// 4. 删除任意位置的元素(STL没有)
void remove(int k) {
h[k] = h[size];
size--;
down(k);
up(k);
}
// 5. 修改任意位置的元素(STL没有)
void update(int k, int x) {
h[k] = x;
down(k);
up(k);
}