栈那节表达式求值,并查集食物链还没做
堆,哈希表不熟,比赛的时候也很少遇到,故没有经常的练习。

目录

  • 单链表[静态]
  • 双链表[静态]
  • 队列
  • 单调栈
  • 单调队列
  • KMP
  • Trie
  • 并查集
  • 哈希表

单链表[静态]

模板题 AcWing 826. 单链表

y神模板:

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

需要注意的是: idx是一直递增的
完整的代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+10;
//head  指向头节点的指针
//e[N] 保存该节点的值
//ne[N]保存该节点的下一个节点的地址
//idx  表示目前分配到哪里的指针
int head,idx;
int e[N],ne[N];
void init()//初始化
{head=-1;idx=0;
}
void  add_to_head(int x)//将x插到头节点
{ne[idx]=head;e[idx]=x;head=idx;idx++;
}
void add(int k,int x)//将x插入到下标是k的点后面
{ne[idx]=ne[k];e[idx]=x;ne[k]=idx;idx++;
}
void remove(int k)//将下标是k的点的后面的点删除
{ne[k]=ne[ne[k]];
}
int main(void)
{int n; cin>>n;init();while(n--){char op; cin>>op;if(op=='H'){int x; cin>>x;add_to_head(x);}if(op=='I'){int k,x; cin>>k>>x;add(k-1,x);}if(op=='D'){int k; cin>>k;if(!k) head=ne[head];remove(k-1);}}for(int i=head;i!=-1;i=ne[i]) cout<<e[i]<<" ";return 0;
}

双链表[静态]

模板题 AcWing 827. 双链表

双链表的初始状态

当我们从链表的最左边插入的时候,是从0的右边插入的。
当我们从链表的最右边插入的时候,是从1的左边插入的。

y神模板:

// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;// 初始化
void init()
{//0是左端点,1是右端点r[0] = 1, l[1] = 0;idx = 2;
}// 在节点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];
}

完整的代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=100010;
int m;
int e[N],l[N],r[N],idx;//在节点a的右边插入一个数
void insert(int a,int x)
{e[idx]=x;l[idx]=a,r[idx]=r[a];l[r[a]]=idx,r[a]=idx++;
} //删除节点
void remove(int a)
{l[r[a]]=l[a];r[l[a]]=r[a];
} void init()
{r[0]=1,l[1]=0;idx=2;
}int main(void)
{cin>>m;init();while(m--){string op; cin>>op;int k,x;if(op=="L"){cin>>x;insert(0,x);}if(op=="R"){cin>>x;insert(l[1],x);}if(op=="D"){cin>>k;remove(k+1);}if(op=="IL"){cin>>k>>x;insert(l[k+1],x);}if(op=="IR"){cin>>k>>x;insert(k+1,x);}}for(int i=r[0];i!=1;i=r[i])  cout<<e[i]<<" ";cout<<endl;
}

模板题 AcWing 828. 模拟栈
3302. 表达式求值 不会

#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
#include <unordered_map>using namespace std;stack<int> num;
stack<char> op;void eval()
{auto b = num.top(); num.pop();auto a = num.top(); num.pop();auto c = op.top(); op.pop();int x;if (c == '+') x = a + b;else if (c == '-') x = a - b;else if (c == '*') x = a * b;else x = a / b;num.push(x);
}int main()
{unordered_map<char, int> pr{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};string str;cin >> str;for (int i = 0; i < str.size(); i ++ ){auto c = str[i];if (isdigit(c)){int x = 0, j = i;while (j < str.size() && isdigit(str[j]))x = x * 10 + str[j ++ ] - '0';i = j - 1;num.push(x);}else if (c == '(') op.push(c);else if (c == ')'){while (op.top() != '(') eval();op.pop();}else{while (op.size() && op.top() != '(' && pr[op.top()] >= pr[c]) eval();op.push(c);}}while (op.size()) eval();cout << num.top() << endl;return 0;
}

y神模板:

// tt表示栈顶
int stk[N], tt = 0;// 向栈顶插入一个数
stk[ ++ tt] = x;// 从栈顶弹出一个数
tt -- ;// 栈顶的值
stk[tt];// 判断栈是否为空
if (tt > 0)
{}

完整代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+10;
int st[N],tt;
int main(void)
{int m; cin>>m;while(m--){string op; cin>>op;if(op=="push"){int x; cin>>x;st[++tt]=x;}if(op=="pop") tt--;if(op=="query") cout<<st[tt]<<endl;if(op=="empty") if(tt>0) cout<<"NO"<<endl;else cout<<"YES"<<endl;}
}

队列

模板题 AcWing 829. 模拟队列

普通队列:

1. 普通队列:
// hh 表示队头,tt表示队尾
int q[N], hh = 0, tt = -1;// 向队尾插入一个数
q[ ++ tt] = x;// 从队头弹出一个数
hh ++ ;// 队头的值
q[hh];// 判断队列是否为空
if (hh <= tt)
{}

完整代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+10;
int q[N],hh,tt=-1;
int main(void)
{int m; cin>>m;while(m--){string op; cin>>op;if(op=="push"){int x; cin>>x;q[++tt]=x;}if(op=="pop") hh++;if(op=="empty"){if(hh<=tt) cout<<"NO"<<endl;else cout<<"YES"<<endl;}if(op=="query") cout<<q[hh]<<endl;}return 0;
}

循环队列

// hh 表示队头,tt表示队尾的后一个位置
int q[N], hh = 0, tt = 0;// 向队尾插入一个数
q[tt ++ ] = x;
if (tt == N) tt = 0;// 从队头弹出一个数
hh ++ ;
if (hh == N) hh = 0;// 队头的值
q[hh];// 判断队列是否为空
if (hh != tt)
{}

单调栈


关于单调栈的文章

模板题 AcWing 830. 单调栈

y神模板:

常见模型:找出每个数左边离它最近的比它大/小的数
int tt = 0;
for (int i = 1; i <= n; i ++ )
{while (tt && check(stk[tt], i)) tt -- ;stk[ ++ tt] = i;
}

完整代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+10;
int st[N],x;
int tt=0;
int main(void)
{int n; cin>>n;while(n--){cin>>x;while(st[tt]>=x) tt--;//栈顶元素大于当前元素,则出栈if(!tt) cout<<"-1"<<" ";;//栈空,输出-1if(tt) cout<<st[tt]<<" ";//非空,输出栈顶元素st[++tt]=x;}return 0;
}

单调队列

模板题 AcWing 154. 滑动窗口

y神模板:

常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{while (hh <= tt && check_out(q[hh])) hh ++ ;  // 判断队头是否滑出窗口while (hh <= tt && check(q[tt], i)) tt -- ;q[ ++ tt] = i;
}

总的代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e6+10;
int a[N],q[N];
int hh,tt;
int n,k;
int main(void)
{cin>>n>>k;for(int i=0;i<n;i++) scanf("%d",&a[i]);hh=0,tt=-1;for(int i=0;i<n;i++)//这里的q是一个递增的序列 , 队首元素就是所求的最小值{if(i-k+1>q[hh]) hh++;//窗口出界了while(hh<=tt&&a[i]<=a[q[tt]]) tt--;//不满足单调性q[++tt]=i;//q保存的是a数组的下标if(i+1>=k) printf("%d ",a[q[hh]]);}cout<<endl;hh=0,tt=-1;for(int i=0;i<n;i++)//这里的q是一个递减的序列{if(i-k+1>q[hh]) hh++;while(hh<=tt&&a[i]>a[q[tt]]) tt--;q[++tt]=i;if(i+1>=k) printf("%d ",a[q[hh]]);}return 0;
}

KMP

模板题 AcWing 831. KMP字符串

y神模板:

// 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];// 匹配成功后的逻辑}
}

总的代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=100010,M=1000010;int n,m;
int ne[N];
char s[M],p[N];
int main(void)
{cin>>n>>p+1>>m>>s+1;//求Next数组:// s[]是模式串,p[]是模板串, n是s的长度,m是p的长度// 为什么要从i = 2开始匹配?因为next[1] = 0,已经确定了,所以应该从i = 2开始匹配。// 注意:next[i]的定义是非平凡的最大后缀等于最大前缀,next[i]必须要小于i// 对于每一个i开始匹配过程for (int i = 2, j = 0; i <= n; i ++ ){// 如果p[i] != p[j + 1]那么,就跳到ne[j]再进行匹配p[i]与p[j + 1],直到p[i] == p[j + 1]或j = 0为止// j一定要大于0,因为j大于0才能跳转到next[j]嘛,ne[0]没有意义while (j && p[i] != p[j + 1]) j = ne[j]; // 如果一直跳转到 p[i] == p[j + 1],那么就可以移动jif (p[i] == p[j + 1]){j ++ ;ne[i] = j;}// 否则就说明i点没有最大后缀等于最大前缀else{ne[i] = 0;}}// 匹配for (int i = 1, j = 0; i <= m; i ++ ){while (j && s[i] != p[j + 1]) j = ne[j];if (s[i] == p[j + 1]) j ++ ;if (j == n){printf("%d ",i-n);j = ne[j];// 匹配成功后的逻辑}}return 0;
}

Trie

Trie的概念: 高效地存储和查找字符串集合的数据结构。

835. Trie字符串统计
143. 最大异或对
240. 食物链 未解决

y神的模板:

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];
}

首先要注意的一点就是: N代表的是输入的字符串总长度。

也就是说,这个Trie树的结点个数和是小于等于N的。

第二点 idx的含义:其实和单链表里的那个含义差不多,如下图我已经用绿笔标出了结点对应的idx的值

需要注意的一点就是我们的每一个点,对应的idx都是唯一的,这样我们再找儿子的过程中就不会有一对多的情形。

第三点son[N] [26] 的含义
很多小伙伴,不知道这个的含义是啥。其是只要带个案例模拟一边你就懂了。
例: son[0][u] 这个的含义就是根节点它的孩子u结点的下标,跟单链表里的next指针其实是一样的。
不过有区别的一点是我们的单链表它的孩子只有一个,是一条链子。
但是我们的Trie是后面可能有26个孩子(因为英文字母只有26个),即有26种路径可以选的单链表。

例: abc

其实只要把字符串的每条路径当成一个单链表,就十分的容易理解了。
最后cnt[p] 的含义
就是该字符串出现的次数。比如上图的: abc 这条字符串单链表已经到头了,那么我们就 cnt[p]++,
p代表的就是c字符所对应的idx,idx我已经说了它和一个字符是唯一对应的,这里的唯一对应不仅是字符对应的关系还有位置(即树的层数)。
因为第1层的" a " 和 第二层的 " a ” 是不同的。
总的代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=100010;
int s[N][26],cnt[N],idx;
void insert(string str)
{int p=0;for(int i=0;str[i];i++){int u=str[i]-'a';if(!s[p][u]) s[p][u]=++idx;p=s[p][u];}cnt[p]++;
}
int query(string str)
{int p=0;for(int i=0;str[i];i++){int u=str[i]-'a';if(!s[p][u]) return 0;//没有找到p=s[p][u];//下一个结点的指针}return cnt[p];
}
int main(void)
{int n; cin>>n;while(n--){string op,str;cin>>op>>str;if(op=="I") insert(str);else  cout<<query(str)<<endl;}
}

还有一点就是我们每次是从根节点开始找的,找到就继续找,没有找到就创建子结点。

并查集

836. 合并集合
837. 连通块中点的数量

y神模板:

(1)朴素并查集:int p[N]; //存储每个点的祖宗节点// 返回x的祖宗节点int find(int x){if (p[x] != x) p[x] = find(p[x]);return p[x];}// 初始化,假定节点编号是1~nfor (int i = 1; i <= n; i ++ ) p[i] = i;// 合并a和b所在的两个集合:p[find(a)] = find(b);(2)维护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~nfor (int i = 1; i <= n; i ++ ){p[i] = i;size[i] = 1;}// 合并a和b所在的两个集合:size[find(b)] += size[find(a)];p[find(a)] = find(b);(3)维护到祖宗节点距离的并查集:int p[N], d[N];//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离// 返回x的祖宗节点int find(int x){if (p[x] != x){int u = find(p[x]);d[x] += d[p[x]];p[x] = u;}return p[x];}// 初始化,假定节点编号是1~nfor (int i = 1; i <= n; i ++ ){p[i] = i;d[i] = 0;}// 合并a和b所在的两个集合:p[find(a)] = find(b);d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量

838. 堆排序
839. 模拟堆

y神模板:

// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1
// ph[k]存储第k个插入的点在堆中的位置
// hp[k]存储堆中下标是k的点是第几个插入的
int h[N], ph[N], hp[N], size;// 交换两个点,及其映射关系
void heap_swap(int a, int b)
{swap(ph[hp[a]],ph[hp[b]]);swap(hp[a], hp[b]);swap(h[a], h[b]);
}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){heap_swap(u, t);down(t);}
}void up(int u)
{while (u / 2 && h[u] < h[u / 2]){heap_swap(u, u / 2);u >>= 1;}
}// O(n)建堆
for (int i = n / 2; i; i -- ) down(i);

哈希表

840. 模拟散列表

一般哈希:

(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;}(2) 开放寻址法int h[N];// 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置int find(int x){int t = (x % N + N) % N;while (h[t] != null && h[t] != x){t ++ ;if (t == N) t = 0;}return t;}

字符串哈希:
841. 字符串哈希
y神模板:

核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64// 初始化
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];
}

第二章 数据结构 【完结】相关推荐

  1. 斗地主AI算法——第二章の数据结构

    上一章我们已经确立了基本的业务逻辑处理流程.从本章开始,我们便进入开发阶段. 首先就是明确我们都需要哪些数据,且它们以怎样的形式存储. 首先从上一章反复提到的手牌权值结构说起,也就是F()的返回值,他 ...

  2. yxc_第二章 数据结构(一)_链表

    写在前面:链表这一部分完结,我觉得链表最重要的还是根据题目来写代码(数组模拟指针),可能是大二上已经学过应试版数据结构的缘故吧.最后,还是希望自己能够持之以恒,在寒假完成算法基础课.(最近几天都睡到了 ...

  3. 第二章 数据结构(二)

    文章目录 Trie 835 Trie 字符串统计 并查集 Trie 高效存储和查找字符串集合的数据结构 有结尾标记,边创建节点,边存储 835 Trie 字符串统计 维护一个字符串集合,支持两种操作: ...

  4. 第二章 数据结构(一)

    文章目录 链表和邻接链表 单链表 826 单链表 双链表 827 双链表 栈与队列 830 单调栈 kmp 831 kmp字符串 链表和邻接链表 数据模拟的速度会快很多,每次 new 一个新的节点,速 ...

  5. yxc_第二章 数据结构(二)_Trie树

    目录 一.基本概念 二.AcWing 835 Trie字符串统计 本题思路: 一.基本概念 接下来的案例类似于邻接矩阵,横轴表示str[ i ] - 'a'(省略了7~25),竖轴表示节点编号.红色的 ...

  6. yxc_第二章 数据结构(一)_kmp算法

    目录 一.思路 二.AcWing 831 KMP字符串 三.时间复杂度 一.思路 当子串P与模式串S匹配一部分时,在两个串的接下来的一个元素不相同,而在已经匹配成功的串中存在前缀等于后缀.此时后缀最后 ...

  7. yxc_第二章 数据结构(一)_栈和队列

    目录 一.栈和队列的代码操作 1.AcWing 828 模拟栈 2.AcWing 829 模拟队列 二.单调栈 1.AcWing 830 单调栈 三.单调队列 1.AcWing 154 滑动窗口 一. ...

  8. python (第二章)数据结构

    文章目录 2.5 对序列使用 +和 × 建立由列表组成的列表 2.6序列的增量赋值(+=和×=) 关于 +=的谜题 补充:extend()方法和+有什么区别呢? 2.7 list.sort方法和内置函 ...

  9. ppk on JavaScript第二章:背景(完结篇)

    无障碍规则 尽管无法预见可能损害一个有脚本网站的可用性的所有情形,但我已经总结出一些可以帮助您在基础上不犯错的规则.不要把它们当作JavaScript和无障碍的终极规则,这只是能防止一些常见低级错误的 ...

最新文章

  1. python3 django配置数据库(mysql)
  2. Python中使用数据库SQLite
  3. Ignite 的helloworld第二弹!(附源码!下载即用)
  4. C# 窗体输入个人信息 存入txt 窗体输出
  5. python与正则表达式(part4)--正则表达式分组
  6. c#中chart绘制曲线,柱状图等
  7. python画二次函数图像的顶点_画二次函数图像的步骤
  8. P1090-合并果子【离散化,队列,时间复杂度O(n)】
  9. 2020蓝桥杯省赛---java---B---10(整数小拼接)
  10. shell基础(上)
  11. Genymotion下载及安装(安卓虚拟机)
  12. SpringCloud微服务实战(一)-简介
  13. mock SpringMVC 测试控制器方法
  14. 洛谷P2024 食物链
  15. Win10系统安装失败?用这种方法一键就解决了
  16. 网上银行显示本服务器只显示,使用企业网上银行时常见报错提示有哪些,怎么解决?...
  17. windows 10 下载工具
  18. iperf3网络测速
  19. 电网企业 财务管理创新
  20. 电脑回收站在哪里?回收站删除的文件怎么恢复?

热门文章

  1. 程序员,选择和努力哪个重要?
  2. cf 1062d 思维 欧拉筛变形
  3. Python函数之初体验
  4. django jquery ajax 知识点
  5. cocos2dx的图片载入
  6. iOS开发UI篇—UITableview控件使用小结
  7. 如何adb shell进入ctia模式
  8. 指针01 - 零基础入门学习C语言41
  9. 《Performance Overhead of Atomic Crosschain Transactions》
  10. [register]-ARMV8-aarch64 部分系统寄存器总结(必背)