常用程式碼模板
阿新 • • 發佈:2020-08-05
單鏈表
// 前項星
// 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);
}
}
- 寬度優先遍歷