AcWing基础算法课Level-2 第二讲 数据结构
单链表
AcWing 826. 单链表3453人打卡
双链表
AcWing 827. 双链表2865人打卡
栈
AcWing 828. 模拟栈3134人打卡
AcWing 3302. 表达式求值332人打卡
队列
AcWing 829. 模拟队列3048人打卡
单调栈
AcWing 830. 单调栈3340人打卡
单调队列
AcWing 154. 滑动窗口3087人打卡
KMP
AcWing 831. KMP字符串2808人打卡
Trie
AcWing 835. Trie字符串统计3026人打卡
AcWing 143. 最大异或对2306人打卡
并查集
AcWing 836. 合并集合3148人打卡
AcWing 837. 连通块中点的数量2897人打卡
AcWing 240. 食物链1901人打卡
堆
AcWing 838. 堆排序2746人打卡
AcWing 839. 模拟堆2023人打卡
哈希表
AcWing 840. 模拟散列表2509人打卡
AcWing 841. 字符串哈希2303人打卡
代码
AcWing 826. 单链表
//单向链表,头部插入,中间插入,删除节点
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int idx, head, nxt[maxn], val[maxn];
void add_head(int x){
val[idx] = x;
nxt[idx] = head;
head = idx++;
}
void add(int k, int x){
val[idx] = x;
nxt[idx] = nxt[k];
nxt[k] = idx++;
}
void remove(int k){
nxt[k] = nxt[nxt[k]];
}
int main(){
head = -1; idx = 1;
int m; cin>>m;
for(int i = 1; i <= m; i++){
string op; cin>>op;
if(op=="D"){
int k; cin>>k;
if(!k)head = nxt[head];
remove(k);
}else if(op=="H"){
int x; cin>>x;
add_head(x);
}else if(op=="I"){
int k, x; cin>>k>>x;
add(k,x);
}
}
for(int i=head; i!=-1; i=nxt[i])
cout<<val[i]<<" ";
return 0;
}
AcWing 827. 双链表
//双向链表__
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int idx=1, l[maxn], r[maxn], val[maxn];
void insertL(int x){
l[idx] = 1; r[idx] = r[1];//idx->pre,next;
l[r[1]] = idx; r[1] = idx;//head->pre,next;
val[idx++] = x;
}
void insertR(int x){
r[idx] = 2; l[idx] = l[2];//idx->pre,next;
r[l[2]] = idx; l[2] = idx;//tail->pre,next;
val[idx++] = x;
}
void remove(int k){
r[l[k]] = r[k]; l[r[k]] = l[k];
}
void insertKL(int k, int x){
l[idx] = l[k]; r[idx] = k;
r[l[k]] = idx; l[k] = idx;
val[idx++] = x;
}
void insertKR(int k, int x){
r[idx] = r[k]; l[idx] = k;
l[r[k]] = idx; r[k] = idx;
val[idx++] = x;
}
int main(){
r[1] = 2; l[2] = 1; idx=3;//init(){ head==1,tail==2; }
int m; cin>>m;
for(int i = 1; i <= m; i++){
string op; cin>>op;
if(op=="L"){
int x; cin>>x; insertL(x);
}else if(op=="R"){
int x; cin>>x; insertR(x);
}else if(op=="D"){
int k; cin>>k; remove(k+2);//number[k] is idx-2;
}else if(op=="IL"){
int k, x; cin>>k>>x; insertKL(k+2,x);
}else if(op=="IR"){
int k, x; cin>>k>>x; insertKR(k+2,x);
}
}
for(int i=r[1]; i!=2; i=r[i])
cout<<val[i]<<" ";
return 0;
}
AcWing 828. 模拟栈
#include<bits/stdc++.h>
using namespace std;
int main(){
stack<int>s;
int m; cin>>m;
for(int i = 1; i <= m; i++){
string op; cin>>op;
if(op=="push"){
int x; cin>>x; s.push(x);
}else if(op=="pop"){
s.pop();
}else if(op=="empty"){
if(s.empty())cout<<"YES\n";
else cout<<"NO\n";
}else if(op=="query"){
cout<<s.top()<<"\n";
}
}
return 0;
}
AcWing 3302. 表达式求值
#include<bits/stdc++.h>
using namespace std;
int Youxianji(char op){
if(op=='*'||op=='/')return 2;
if(op=='+'||op=='-')return 1;
if(op=='(')return 0;
}
void ToHouzhui(string str, string& ans){
stack<char>stk;
ans = "";
int i =0;
while(i<str.size()){
if(str[i]>='0' && str[i]<='9'){
//ans += str[i];
int t = 0;
while(str[i]>='0' && str[i]<='9'){
t = t*10+str[i]-'0';
i++;
}
ans += to_string(t)+".";
}else{
if(stk.empty()){
stk.push(str[i]);
}else if(str[i]=='('){
stk.push(str[i]);
}else if(str[i]==')'){
while(stk.top()!='('){
ans += stk.top();
stk.pop();
}
stk.pop();
}else{
while(Youxianji(stk.top())>=Youxianji(str[i])){
ans += stk.top();
stk.pop();
if(stk.empty())break;
}
stk.push(str[i]);
}
i++;
}
}
while(stk.size()){
ans += stk.top();
stk.pop();
}
}
int culate(string str){
stack<int>stk;
int ans = 0, i = 0;
while(i < str.size()){
if(str[i]>='0'&&str[i]<='9'){
//stk.push(str[i]-'0');
int t = 0;
while(str[i]!='.'){
t = t*10+str[i]-'0';
i++;
//cout<<t<<endl;
}
i++;
//cout<<t<<endl;
stk.push(t);
}else{
int a = stk.top(); stk.pop();
int b = stk.top(); stk.pop();
int tmp;
if(str[i]=='+')tmp = b+a;
if(str[i]=='-')tmp = b-a;
if(str[i]=='*')tmp = b*a;
if(str[i]=='/')tmp = b/a;
stk.push(tmp);
i++;
}
}
return stk.top();
}
int main(){
string s,t; cin>>s;
ToHouzhui(s,t);
cout<<culate(t)<<endl;
return 0;
}
AcWing 829. 模拟队列
#include<bits/stdc++.h>
using namespace std;
int main(){
queue<int>q;
int m; cin>>m;
for(int i = 1; i <= m; i++){
string op; cin>>op;
if(op=="push"){
int x; cin>>x; q.push(x);
}else if(op=="pop"){
q.pop();
}else if(op=="empty"){
if(q.empty())cout<<"YES\n";
else cout<<"NO\n";
}else if(op=="query"){
cout<<q.front()<<"\n";
}
}
return 0;
}
AcWing 830. 单调栈
/*
单调栈: 就是栈里面存的元素是单调递增(减)的。
题意:输出第i个数左边第一个比它小的元素。
思路:对于当前数a[i],左边比他大的元素对后面的数而言都没有意义,因为如果小也是先经过a[i]所以肯定a[i]先被选中,直接出栈就行,维护单调啊递增的栈,来找比a[i]小的第一个元素。
*/
#include<bits/stdc++.h>
using namespace std;
int main(){
stack<int>s;
int n; cin>>n;
for(int i = 1; i <= n; i++){
int x; cin>>x;
while(s.size() && s.top()>=x)s.pop();//把栈里比他大的元素都出栈
if(s.size()==0)cout<<"-1 ";//如果没有比他小的就-1
else cout<<s.top()<<" ";//否则第一个比他小的
s.push(x);//把该点加入栈
}
return 0;
}
AcWing 154. 滑动窗口
/*
题意:给出一个长为n的数组,求从左到右每k个数中的最大值和最小值。
思路:
+ 对于最小值,维护一个单调递增队列,队头是最小元素,每次进来一个数如果小于队尾就一直队尾出队(因为这些数不会是之后k个中的最小值),然后把队头在k个之前的元素都出队掉(所以维护下标来知道哪些是k个之前的)。
+ 同样的对于最大值,维护一个单调递减队列,如果队尾小于他就出队。
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int a[maxn];
int main(){
vector<int>mi,mx;
int n, k; cin>>n>>k;
for(int i = 1; i <= n; i++)cin>>a[i];
//单调递增队列求最小值
deque<int>q;
for(int i = 1; i <= n; i++){
if(q.size() && q.front()<=i-k)q.pop_front();//k个之前的
while(q.size() && a[q.back()]>=a[i])q.pop_back();//队尾出队
q.push_back(i);
if(i>=k)cout<<a[q.front()]<<" ";
}
cout<<"\n";
//单调递减队列求最大值
q.clear();
for(int i = 1; i <= n; i++){
if(q.size() && q.front()<=i-k)q.pop_front();//k个之前的
while(q.size() && a[q.back()]<=a[i])q.pop_back();//队尾出队
q.push_back(i);
if(i>=k)cout<<a[q.front()]<<" ";
}
return 0;
}
AcWing 831. KMP字符串
/*
+ kmp概念:用于在主字符串中查找模式字符串的位置O(n+m),朴素O(n*m)
+ next概念:前缀函数next[i]表示"模式串中以i结尾的非前缀子串"与"模式串的前缀"能匹配的最大长度(即最长能匹配前缀子串结尾字符的下标,或者说前缀集合与后缀集合的交集中最长元素的长度)
+ next求法:双指针法枚举[2,n],j表示上一个的前缀,如果j+1不等于i,那就让j=nxt[j]去找更前面的,直到匹配到i为止。
+ kmp求法:枚举s字符串,如果s[i]和p[j]不相等了,那就让j=nxt[j],去找上一个,直到匹配到和s[i]相等为止,让后继续匹配下一个,如果匹配到了j的全部长度,那就是答案了。
*/
/*
题意:给出一个长串s和子串p,求p在s中出现的所有位置的起始下标
思路:不停的让s[i]和p[j]匹配就行。
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int nxt[maxn];
void pre_next(string p){
int j = 0; //j表示到i-1为止的最长前缀的下标
for(int i = 2; i < p.size(); i++){
//用到j为止的最长公共前缀的下标,尝试去匹配下一个字母
while(j && p[i]!=p[j+1])j = nxt[j];
if(p[i]==p[j+1])j++;
nxt[i] = j;
}
}
void kmp(string s, string p){
int j = 0;
for(int i = 1; i <= s.size()-1; i++){
while(j && s[i]!=p[j+1])j = nxt[j];//不停的去找直到s[i]能匹配上下一个
if(s[i]==p[j+1])j++;//匹配上了就加一
if(j==p.size()-1){//匹配完了
int t = i-p.size()+1;
cout<<t<<" ";
j = nxt[j];
}
}
}
int main(){
int n, m; string s,p;
cin>>n>>p>>m>>s;
s="0"+s; p="0"+p;
pre_next(p);
//for(int i = 0; i <= n; i++)cout<<nxt[i]<<" ";
kmp(s,p);
return 0;
}
AcWing 835. Trie字符串统计
//字典树,有模板就能打
//维护一个集合,支持插入字符串,以及查询字符串在集合中出现过多少次。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int tire[maxn][26], val[maxn], tot;
void build(){
tot = 1; //root
memset(tire,0,sizeof(tire));
memset(val,0,sizeof(val));
}
void insert(string s){
int u = 1;
for(int i = 0; i < s.size(); i++){
int c = s[i]-'0';
if(tire[u][c]==0){
tire[u][c] = ++tot;
}
u = tire[u][c];
}
val[u]++;
}
int query(string s){
int u = 1;
for(int i = 0; i < s.size(); i++){
int c = s[i]-'0';
if(tire[u][c]==0)return 0;
u = tire[u][c];
}
return val[u];
}
int main(){
int n;
scanf("%d",&n);
build();
bool ans = false;
for(int i = 1; i <= n; i++){
string op, s; cin>>op>>s;
if(op=="I"){
insert(s);
}else if(op=="Q"){
cout<<query(s)<<"\n";
}
}
return 0;
}
AcWing 143. 最大异或对
#include<iostream>
using namespace std;
const int maxn = 1e5+10;
int tot, tire[maxn*32][2];
void insert(int x){
int u = 0;
for(int i = 31; i >= 0; i--){
int c = (x>>i)&1;
if(!tire[u][c])tire[u][c] = ++tot;
u = tire[u][c];
}
}
int query(int x){
int u = 0, ans = 0;
for(int i = 31; i >= 0; i--){
int c = (x>>i)&1;
if(tire[u][!c])u = tire[u][!c], ans = (ans<<1)|1;
else u = tire[u][c], ans = ans<<1;
}
return ans;
}
int a[maxn];
int main(){
ios::sync_with_stdio(false);
int n; cin>>n;
for(int i = 1; i <= n; i++){
cin>>a[i]; insert(a[i]);
}
int ans = 0;
for(int i = 1; i <= n; i++)
ans = max(ans, query(a[i]));
cout<<ans<<'\n';
return 0;
}
AcWing 836. 合并集合
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int fa[maxn+10];
void init(int n){for(int i = 1; i <= n; i++)fa[i]=i;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void merge(int x, int y){x=find(x);y=find(y);if(x!=y)fa[x]=y;}
int count(int n){int cnt=0; for(int i = 1; i <= n; i++)if(fa[i]==i)cnt++;return cnt;}
int main(){
ios::sync_with_stdio(false);
int n, m; cin>>n>>m;
init(n);
for(int i = 1; i <= m; i++){
string op; int x, y;
cin>>op>>x>>y;
if(op=="M"){
merge(x,y);
}else if(op=="Q"){
if(find(x)==find(y))cout<<"Yes\n";
else cout<<"No\n";
}
}
return 0;
}
AcWing 837. 连通块中点的数量
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int fa[maxn+10], sz[maxn+10];
void init(int n){for(int i = 1; i <= n; i++){fa[i]=i;sz[i]=1;}}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void merge(int x, int y){x=find(x);y=find(y);if(x!=y){fa[x]=y;sz[y]+=sz[x];}}
int count(int n){int cnt=0; for(int i = 1; i <= n; i++)if(fa[i]==i)cnt++;return cnt;}
int main(){
ios::sync_with_stdio(false);
int n, m; cin>>n>>m;
init(n);
for(int i = 1; i <= m; i++){
string op; cin>>op;
if(op=="C"){
int x, y; cin>>x>>y;
merge(x,y);
}else if(op=="Q1"){
int x, y; cin>>x>>y;
if(find(x)==find(y))cout<<"Yes\n";
else cout<<"No\n";
}else if(op=="Q2"){
int x; cin>>x;
cout<<sz[find(x)]<<"\n";
}
}
return 0;
}
AcWing 240. 食物链
#include<iostream>
#include<algorithm>
using namespace std;
int ans = 0;
int fa[50010*3];
void init(int n){ for(int i = 1; i <= n; i++)fa[i]=i; }
int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); }
void merge(int x, int y){ x=find(x);y=find(y);if(x!=y)fa[x]=y;}
int some(int x, int y){ return find(x)==find(y); }
int main(){
int n, m;
cin>>n>>m;
init(n*3);
for(int i = 1; i <= m; i++){
int op, x, y;
cin>>op>>x>>y;
if(x>n||y>n){ans++;continue;}//2)当前的话中X或Y比N大,就是假话
if(op==2&&x==y){ans++;continue;}//3)当前的话表示X吃X,就是假话
if(op==1){
if(some(x,y+n)||some(y,x+n))ans++;//如果x吃y或者y吃x,就不是同类
else{
merge(x,y);//x和y是同类
merge(x+n,y+n);//能吃x和y的也是同类
merge(x+n*2,y+n*2);//x和y能吃的也是同类
}
}else{
if(some(x,y)||some(y,x+n))ans++;//如果x和y是同类或者y吃x
else{
merge(x,y+n);//x和吃y的连起来
merge(x+n,y+n*2);//能吃x的和被y吃的连起来(三种动物之间的关系啊)
merge(x+n*2,y);//x能吃的和y连起来
}
}
}
cout<<ans<<"\n";
return 0;
}
AcWing 838. 堆排序
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int a[maxn];
int main(){
ios::sync_with_stdio(false);
int n, m; cin>>n>>m;
for(int i = 1; i <= n; i++)cin>>a[i];
sort(a+1,a+n+1);
for(int i = 1; i <= m; i++)cout<<a[i]<<" ";
return 0;
}
AcWing 839. 模拟堆
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int a[maxn], tot;
int main(){
int n; cin>>n;
multiset<int>se;
for(int i = 1; i <= n; i++){
string op; cin>>op;
if(op=="I"){
int x; cin>>x; se.insert(x);
a[++tot] = x;
}else if(op=="PM"){
cout<<(*se.begin())<<"\n";
}else if(op=="DM"){
se.erase(se.begin());
}else if(op=="D"){
int k; cin>>k;
se.erase(se.find(a[k]));
}else if(op=="C"){
int k, x; cin>>k>>x;
se.erase(se.find(a[k]));
se.insert(x);
a[k] = x;
}
}
return 0;
}
AcWing 840. 模拟散列表
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int main(){
int n; cin>>n;
set<int>se;
for(int i = 1; i <= n; i++){
string op; cin>>op;
int x; cin>>x;
if(op=="I"){
se.insert(x);
}else if(op=="Q"){
if(se.count(x))cout<<"Yes\n";
else cout<<"No\n";
}
}
return 0;
}
AcWing 841. 字符串哈希
//字符串前缀哈希
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
LL h[maxn], p[maxn];
void init(string s,int P){ //P为基数
p[0] = 1; h[0] = 0;
for(int i = 0; i < s.size(); i++){
p[i+1] = p[i]*P;
h[i+1] = h[i]*P+s[i];
}
}
LL query(int l, int r){
return h[r]-h[l-1]*p[r-l+1];
}
int main(){
ios::sync_with_stdio(false);
int n, m; cin>>n>>m;
string s; cin>>s; init(s,131);
for(int i = 1; i <= m; i++){
int l1, r1, l2, r2;
cin>>l1>>r1>>l2>>r2;
if(query(l1,r1)==query(l2,r2))cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}