本文包括集中常见的数据结构,如链表,队列,栈。
链表
单链表
单链表是一种基本的链表,支持基本的插入,删除操作。
const int N = 100010;
int idx,ne[N],head,value[N];
void init()//初始化
{
idx=0;
head=-1;
}
void insert(int x)//在头结点后插入
{
value[idx]=x;
ne[idx]=head,head=idx,idx++;
}
void insert_m(int k,int x)//在下标为k的位置后插入x
{
value[idx]=x,ne[idx]=ne[k],ne[k]=idx++;
}
void remove_(int k)//删除头结点
{
ne[k]=ne[ne[k]];
}
我们定义链表的头节点为head,idx为当前用到节点的值,e[N]数组用于存放结点的值,ne[N]数组用于存放当前结点的next指针,初始化操作我们让head为-1,idx为0。空结点下表为-1。
插入操作:
如果我们希望把一个数字插到头结点,我们可以分为两步(先将x的值赋值给e[idx[,第一步将要插入的结点指向数值为3的这个结点,也就是让ne[idx]=head,head就是指向数值为3的结点的指针,然后将head原先的指针断开,再将idx加一,也就是head=idx++。
如果是插入到下标为k的结点,如图先让e[idx]=x,将x的值赋给这个插入结点然后第一步让结点指向结点3,然后将2号结点的指针指向插入结点即可完成插入。要注意的是,在第k个插入结点后插入的操作必须要先将插入结点指针指向3结点,因为如果先将2指向插入结点,3号结点就找不到了
双链表
双链表的操作与单链表差别不大,主要是为了更快的寻找到前驱元素增加的左指针,在初始化中我们定义下标为0的为head结点,下标为1的为tail结点,刚刚开始我们的idx就为2,因为我们刚刚开始双链表中就有两个结点
const int N=1e6+10;
int value[N],l[N],r[N],idx;
void Init()//初始化双链表,我们让下标为0的点为头结点,下标为1的点是尾结点
{
r[0]=1;
l[1]=0;
idx=2;
}
void add(int k,int x)//在编号为k的点的右边插入一个数
{
value[idx]=x;
l[idx]=k;
r[idx]=r[k];
l[r[k]]=idx;
r[k]=idx;
idx++;
}
void remove(int k )//删除第k个点
{
r[l[k]]=r[k];
l[r[k]]=l[k];
}
栈
栈是一个操作受限的线性表,其只支持在栈顶进行入栈和出栈操作(first-in last-out)
const int N=1e6+10;
int stack[N],tt;
void Init()//栈的初始化
{
tt=-1;
}
void insert(int x)//入栈
{
stack[++tt]=x;;
}
void pop()//出栈
{
tt--;
}
int query()//返回栈顶元素
{
return stack[tt];
}
bool judge()//判断栈是否为空
{
if(tt>=0)
{
printf("Not Empty");
}
else
printf("Empty");
}
单调栈(模板题)
单调栈的主要应用有:求数列中每个元素X的右边第一个大于元素X的新元素Y
求数列中每个元素X的左边第一个小于元素X的新元素Y
给一个数组,返回一个大小相同的数组,返回的数组的第i个位置的值是对于原数组中的第i个元素,至少往右走多少步,才能遇到一个比自己大的元素
给一个数组,返回一个大小相同的数组,返回的数组的第i个位置的值是对于原数组中的第i个元素,至少往左走多少步,才能遇到一个比自己小的元素
如果不存在,则输出-1
一下是求一个数组中一个数组左边距离最近的小于它的数的题解。
#include <iostream>
using namespace std;
const int N=10000;
int stack[N],tt;//栈和栈顶元素
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
while(tt&&stack[tt]>=x) tt--;
if(tt) cout<<stack[tt]<<" ";
else cout<<"-1"<<" ";
stack[++tt]=x;
}
return 0;
}
队列
队列是一种特殊线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
using namespace std;
const int N=1e6+10;
int queue[N],hh,tt;
void Init()//队列的初始化
{
hh=-1;
tt=-1;
}
void push(int x)//入队
{
queue[++tt]=x;
}
void pop()//出队
{
hh++;
}
int get()//返回队头元素
{
return queue[hh];
}
单调队列(滑动窗口)
#include<cstdio>
#include<iostream>
using namespace std;
const int N = 1e6+10;
int a[N],q[N],hh = 0,tt = -1;
int main()
{
int n,k;
cin >> n >> k;
for(int i=0;i<n;i++)scanf("%d",&a[i]);
for(int i=0;i<n;i++)
{
if(hh <= tt && (i - k + 1) > q[hh])hh++;
while(hh <= tt && a[i] <= a[q[tt]])tt--;
q[++tt] = i;
if(i-k+1>=0)
printf("%d ",a[q[hh]]);
}
puts("");
hh = 0,tt = -1,q[0] = 0;
for(int i=0;i<n;i++)
{
if(hh <= tt && (i - k + 1) > q[hh])hh++;
while(hh <= tt && a[i] >= a[q[tt]])tt--;
q[++tt] = i;
if(i-k+1>=0)
printf("%d ",a[q[hh]]);
}
return 0;
}