Data Structure And Algorithms
    
  
      
    
    
    
      
        
单链表与双链表
静态链表实现
单链表
核心实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | const int N = 100010; int ne[N], e[N], head, idx;
  void init(){     head=-1;  }
  void addHead(int x){     e[idx]=x;     ne[idx]=head;     head=idx;       idx++; }
  void add(int x, int k){     e[idx]=x;     ne[idx]=ne[k];     ne[k]=idx;     idx++; }
  void del(int k){     ne[k]=ne[ne[k]]; }
   | 
 
练习题型
🍑 ;826.单链表
1 2 3 4
   | // 1.删除第k个插入节点,则其下标idx=k-1 // 2.删除头节点特殊处理:head=ne[head];
  // 补充:我真的会谢,输入输出有问题)
   | 
 
双链表
练习题型
827.双链表
没有领悟到双链表的精髓呢!
1 2 3 4
   | // 1.功能上四个函数,但是完全可以压缩为两个函数的功能! // ->add(),del() // (体现双链表精华的地方,l[k]与r[k]左右获取) // 2.不要分类讨论,就要统一处理!
   | 
 
1 2 3 4 5 6
   |  void init(){     head=0, tail=1;     r[head]=tail, l[tail]=head;     idx+=2; }
 
  | 
 
单调栈
常见模型:找出每个数左边离它最近的比它大/小的数
练习题型
828.模拟栈
⭐830.单调栈:
栈内容递增
1 2 3 4 5 6 7
   | while(m--){     int x; scanf("%d",&x);     while(top!=-1&&s[top]>=x) top--;     if(top!=-1) printf("%d ", s[top]);     else printf("-1 ");     s[++top]=x; }
  | 
 
单调队列
常见模型:找出滑动窗口中的最大/小值
练习模拟
829.模拟队列
nc,scanf到现在都不知道加&吗
1 2 3 4 5 6 7
   | void push(int x){     q[tail++]=x; } void pop(){     if(head+1>=tail) head=tail;     else head++; }
  | 
 
🍍154.滑动窗口
(1)hh=0,tt=-1 (2)q[i]存储下标 (3)单增
1 2 3 4 5 6 7 8 9
   | int hh = 0, tt = -1;
  for(int i = 0; i < n; i++){     if(hh <= tt && i - k + 1 > q[hh]) hh++;     while(hh <= tt && a[q[tt]] >= a[i]) tt--;     q[++tt] = a[i];
      if(i >= k-1) printf("%d ", a[q[hh]]); }
   | 
 
KMP
Trie树
高效存储/查找字符串集合
核心实现
835.Trie字符串统计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | int son[N][26], cnt[N], idx; 
  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;     }     return cnt[p]; }
   | 
 
练习题型
143.最大异或对
(1)移位运算 (2)i=30->0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | void insert(int x){     int p=0;     for(int i=30;i>=0;i--){         int u=x>>i&1;           if(!son[p][u]) son[p][u]=++idx;         p=son[p][u];     } } int query(int x){     int p=0, res=0;     for(int i=30;i>=0;i--){         int u=x>>i&1;         if(son[p][!u]){             res+=1<<i;               p=son[p][!u];         }         else p=son[p][u];     }     return res; }
  | 
 
并查集
(原生并查集)快速查找两点是否同一个集合;快速合并两个集合
核心实现
1 2 3 4 5
   |  int find(int x){     if(x!=p[x])p[x]=find(p[x]);     return p[x]; }
 
  | 
 
变形题型
837.连通块中点的数量
1 2 3 4
   |  if(find(a)==find(b)) continue; size[find(b)]+=size[find(a)]; p[find(a)]=find(b);
 
  | 
 
🌻240.食物链
(被我写成模拟题>_<
根节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
   |  int p[N], d[N]; int find(int x){     if(x != p[x]){         int u = find(p[x]);         d[x] += d[p[x]];           p[x] = u;     }     return p[x]; }
  int main(){     int n, k, res=0;     scanf("%d%d", &n, &k);          for(int i=1; i<=n; i++) p[i] = i;     while(k--){         if(a>n||b>n) res++;         int pa = find(a), pb = find(b);         if(op == 1){             if(pa == pb && (d[a] - d[b])%3!=0) res++;             else if(pa!=pb){                 p[pa] = pb; d[pa] = d[b] - d[a];                                }         }         else if(op == 2){             if(pa == pb &&(d[a] - d[b] - 1)%3!=0) res++;             else if(pa!=pb){                 p[pa] = pb; d[pa] = d[b] - d[a] + 1;                              }         }     }     printf("%d\n", res); }
 
  | 
 
堆
插入数;求最小值与删除最小值;删除修改任意元素
核心实现
1 2 3
   | for(int i=n/2;i;i--){     down(i); }
  | 
 
复杂度说明(利用高中的数列求和知识):

)
)
%3Cn*1=n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   |  void down(int idx){     int t=idx;     if(2*t<=size&&h[2*t]<h[idx]) t=2*idx;     if(2*t+1<=size&&h[2*t+1]<h[idx]) t=2*idx+1;     if(t!=idx){         swap(h[t], h[idx]);         down(t);     } }
  void up(int idx){     while(idx/2 && h[idx/2]>h[idx]){         swap(h[idx], h[idx/2]);         idx/=2;     } }
 
  | 
 
1 2 3 4 5
   | while(m--){     printf("%d ",h[1]);       head[1]=head[size--];      down(1);  }
  | 
 
进阶实现
维护数插入的次序;用途:Dijkstra算法
1 2 3 4 5 6 7 8 9
   | 
  int hp[N], ph[N];
  void head_swap(int a, int b){     swap(ph[hp[a]], ph[hp[b]]);     swap(hp[a], hp[b]);     swap(h[a], h[b]); }
 
  | 
 
练习题型
838.堆排序
839.模拟堆
哈希表
哈希表:
1.存储结构:开放寻址法,拉链法
2.字符串哈希方式
注: 取模的数应为质数
存储结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   |  void insert(int x){     int k = (x % N + N) % N;       e[idx] = x;     ne[idx] = h[k];     h[k] = idx;     idx ++; } bool query(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; }
 
  memset(h, -1, sizeof h);  
 
  | 
 
- 开放寻址法
一般,INT_MAX=0x3f3f3f3f 
1 2 3 4 5 6 7 8 9 10 11 12
   | const int N = 200003, null = 0x3f3f3f3f; 
  int find(int x){     int k = (x % N + N) % N;     while(h[k]!=null && h[k]!=x){         k++;         if(k==N) k=0;     }     return k; }
  memset(h, 0x3f, sizeof h); 
   | 
 
字符串前缀哈希法
p = 131或13331,Q = 2^64 (unsigned long long)
用于快速判断两个字符串是否相等
- 
前缀和公式

 
- 
区间公式

 
841.字符串哈希
不能映射为0。否则会冲突
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
   | typedef unsigned long long ull; int P = 131; ull h[N], p[N];  
 
  ull get(int a, int b){     return h[b] - h[a-1] * p[b-a+1]; }
 
  int main(){     int n, m;     scanf("%d%d", &n, &m);          char str[N];     scanf("%s", str);          p[0]=1;     for(int i=1; i<=n; i++){         p[i] = p[i-1] * P;         h[i] = h[i-1] * P + str[i-1];     }          while(m--){         int a, b, c, d;         scanf("%d%d%d%d",&a, &b, &c, &d);                  if(get(a, b)==get(c, d)) puts("Yes");         else puts("No");     }    }
   | 
 
STL
vector
- 倍增: 系统为某一程序分配空间时,所需时间与空间大小无关,与申请次数有关