具体做题感悟与细节:参考个人空间 - AcWing
链表
①单链表(无头)
// 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表示当前用到了哪个节点 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]; }
栈与队列
①栈
// tt表示栈顶 int stk[N], tt = 0; // 向栈顶插入一个数 stk[ ++ tt] = x; // 从栈顶弹出一个数 tt -- ; // 栈顶的值 stk[tt]; // 判断栈是否为空 if (tt > 0) { }
单调栈
常见模型:找出每个数左边离它最近的比它大/小的数 int tt = 0; for (int i = 1; i <= n; i ++ ) { while (tt && check(stk[tt], i)) tt -- ; stk[ ++ tt] = i; }
②队列
1)普通队列
// hh 表示队头,tt表示队尾 int q[N], hh = 0, tt = -1; // 向队尾插入一个数 q[ ++ tt] = x; // 从队头弹出一个数 hh ++ ; // 队头的值 q[hh]; // 判断队列是否为空 if (hh <= tt) { }
2)循环队列
// 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) { }
3)单调队列
常见模型:找出滑动窗口中的最大值/最小值 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; }
KMP
// 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<iostream> #include<stack> #include<unordered_map> #include<string> #include<algorithm> using namespace std; const int N=100010; stack<char>op; stack<int>num; void eval() { int res; auto b=num.top();num.pop();//实际上是第二个操作数!!! auto a=num.top();num.pop(); auto c=op.top();op.pop(); if(c=='+') res=a+b; else if(c=='-')res=a-b; else if(c=='*')res=a*b; else res=a/b; num.push(res); } 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==')')//右括号的话,则一直进行eval运算,直到遇到'(' { while(op.top()!='(')eval(); op.pop();//将‘(’弹出 } else {//当扫到一个运算符的时候,要让op栈内的优先级较高(or同级)者先运算完--->才能让新的运算符入栈,所以代码如下 while(op.size()&&pr[op.top()]>=pr[c])eval();//注意:这边是>= op.push(c); } } while(op.size())eval(); cout<<num.top()<<endl; return 0; }