常用代码模板

单链表


// 前项星
// 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表示当前用到了那个节点

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

// 在节点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];
}


// stk[0] 不使用
// top 表示栈顶
int stk[N], top = 0;

//向栈顶插入
stk[ ++ top] = x;

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

// 栈顶的值
stk[top];

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

}

队列

// 普通队列
// head 指向队头元素, tail指向队尾元素
int q[N], head = 0, tail = -1;

// 队尾插入
q[++ tail] = x;

// 队头弹出
head ++;

// 队头的值ds
q[head];

// 判断队列是否为空
if (head <= tail) {

}

// 循环队列 (少用, 队空和队满的判别条件容易混淆)
// head表示队头, tail 表示队尾的下一个位置
int q[N], head = 0, tail = 0;

// 向队尾插入一个数
q[tail] = x;
tail = (tail + 1) % N;

// 队头弹出一个数
head = (head + 1) % N;
// 队头的值
q[head];


//判断队列是否为空
if (head != tail) {

}

单调栈

int top = 0;
for (int i = 1; i <= n; ++ i) {
    while (top && check(stk[top], i)) top --;
    stk[++ top] = i;
}

单调队列

int he = 0, ta = -1;
for (int i = 0; i < n; i ++) {
    while (he <= ta && check_out(q[he])) he ++;
    while (he <= ta && check(q[ta], i)) ta --;
    q[++ ta] = i;
}

KMP


// s[1,2,...,n]是长文本, p[1,2,...,m]是模式串 

// 求模式串的Next数组
nxt[1] = 0;
for (int i = 2, j = 0; i <= m; ++ i) {
    while (j && p[i] != p[j + 1])
        j = nxt[j];
    if (p[i] == p[j + 1])
        ++ j;
    nxt[i] = j;
}

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

// pi[0,1,...,n-1] 表示s[0,...,n]最长的相等的真前缀与真后缀的长度
vector<int> prefix_function(string s) {
    int n = (int)s.length();
    vector<int> pi(n);
    for (int i = 1; i < n; ++ i) {
        int j = pi[i - 1];
        while (j > 0 && s[i] != s[j])
            j = pi[j - 1];
        if (s[i] == s[j])
            ++j
        pi[i] =j;
    }
    return pi;
}

Trie树

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

//插入一个字符串
void insert(char *str, int l) {
    int p = 0;
    for (int i = 0; i < l; ++ 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 l) {
    int p = 0;
    for (int i = 0; i < l; ++ i) {
        int u = str[i] -'a';
        if (!son[p][u])
            return 0;
        p = son[p][u];
    }
    return cnt[p];
}

并查集

const int N = 10000; // 
namespace union_set {
    int father[N], size[N];
    int _find(int x) {
        int temp = x;
        while (x != father[x])
            x = father[x];
        while (temp != father[temp]) { // 路径压缩
            int z = father[temp];
            father[temp] = x;
            temp = z;
        }
        return x;
    }
    void _union(int a, int b) {
        int fa = _find(a);
        int fb = _find(b);
        father[fb] = fa;
        size[fa] += size[fb];
    }
}

// 大根堆
const int MAXN = 1000;
int heap[MAXN], n = 10;
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[i] < heap[j])
            swap(heap[i], heap[j]);
        else
            break;
        i = j;
        j = i * 2;
    }
}
void upAdjust(int low, int high) {
    int i = high, j = i / 2;
    while (j >= low) {
        if (heap[j] < heap[i])
            swap(heap[i], heap[j]);
        else
            break;
        i = j;
        j = i / 2;
    }
}
void insert(int x) {
    heap[++n] = x;
    upAdjust(1, n);
}
void deleteTop() {
    heap[1] = heap[n--];
    downAdjust(1, n);
}
void heapSort() {
    createHeap();
    for (int i = n / 2; i >= 1; -- i) {
        swap(heap[i], heap[1]);
        downAdjust(1, i - 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;
}
// 如果实现删除, 只要用一个标记数组标记是否删除,不需要真正从内存中删除
  • 开放寻址法
int h[N];
// 如果x在哈希表中,返回x的下标,如果x不在哈希表中,返回x应该插入的位置
int find(int x) {
    int t = (x % N + N) % N;
    while (h[t] != INF && 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;
// h[k]存储字符串前k个字母的哈希值, p[k]存储P^k mod 2^64
ULL h[N], p[N];
// 初始化
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];
}

树与图的存储

  • 邻接矩阵

G[a][b] 表示a->b

  • 邻接表
// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;

// 加一条边
void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
// 初始化
idx = 0;
memset(h, -1, sizeof(h));

树与图的存储

时间复杂度\(O(n+m)\)

  • 深度优先遍历
int dfs(int u) {
    st[u] = true;
    for (int i = h[u];i != -1; ++i) {
        int j = e[i];
        if (!st[j])
            dfs(j);
    }
}
  • 宽度优先遍历
posted @ 2020-08-05 21:54  xcjm  阅读(377)  评论(0)    收藏  举报