1. 程式人生 > 實用技巧 >常用程式碼模板

常用程式碼模板

單鏈表


// 前項星
// 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);
    }
}
  • 寬度優先遍歷