第二章:数据结构
2-1-1 单链表
(因为传统链式存储需要大量使用New函数,会带来大量时间上的开销,所以我们用数组模拟)
(通常情况下,数据规模1e6)
#include<iostream>
using namespace std;
const int N=100010;
//head表示头结点的下标
//e[i]表示结点i的值
//ne[i]表示结点i的next指针是多少
//idx存储当前已经用到了哪个点
int head,e[N],ne[N],idx;
//初始化
void init(){
head=-1;//指向空结点
idx=0;
}
//头插法:将x插到头结点
void add_to_head(int x){
e[idx]=x,ne[idx]=head,head=idx,idx++;
}
//将x插到下标是k的结点后面
void add(int k,int x){
e[idx]=x;
ne[idx]=ne[k];
ne[k]=idx;
idx++:
}
//将下标是k的点后面的点删掉
void remove(int k){
ne[k]=ne[ne[k]];
}
int main(){
}
/*双链表*/
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int m;
int e[N],l[N],r[N],idx;
//初始化
void init(){
//0表示左端点,1表示右端点
r[0]=1;l[1]=0;
idx=2;
}
//在下标是k的点的右边插入x
//如果是在下标为k的点的左边插入X,直接调用add(k-1,x)
void add(int k,int x){
e[idx]=x;
r[idx]=r[k];
l[idx]=k;
l[r[k]]idx;
r[k]=idx;
}
//删除第k个点
void remove(int k){
r[l[k]]=r[k];
l[r[k]]=l[k];
}
int main(){
}
const int N=100010;
//定义栈
int stk[N],tt;
//插入
stk[++tt] = x;
//弹出
tt--;
//判断栈是否为空
if (tt >0) not empty
else empty
//取栈顶元素
stk[tt];
//定义队列
int q[N],hh,tt=-1;
//在队尾插入元素,在队头弹出元素
//插入
q[++tt]=x;
//弹出
hh++;
//判断队列是否为空
if(hh<=tt) not empty
else empty
//取出队头元素
q[hh]
2-2-5 单调栈
常见题型:给出一个序列,找出每个数左边(或右边)满足某个性质最近的数
暴力做法:
for(int i=0;i<n;i++){
for(int j=i-1;j>=0;j--){
if(a[j]<a[i]){
cout<<j<<endl;
break;
}
}
}
#include<iostream>
using namespace std;
const int N=100010;
int n;
int stk[N],tt;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=0;i<n;i++){
int x;
cin>>x;
while(tt && stk[tt]>=x) tt--;//一直出栈,直到找到第一个比栈顶小的元素
if(tt) cout<<stk[tt]<<" ";//栈不空,直接输出栈顶
else cout<<-1<<' ';//不存在
stk[++tt]=x;//该数入栈
}
return 0;
}
2-2-6 单调队列
求滑动窗口的最大值最小值
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int n,k;
int a[N]