前言
使用指针+结构体的方式动态实现栈、链表等数据结构时都需要new Node;容易因此(new Node)TLE,处于效率考虑,刷题时一般不采用这种动态的方式。虽然可以通过直接初始化n个Node进行改进,但是那种方式本质就和数组模拟差不多。
一、静态栈–静态队列–静态链表
栈
const int N = 1e5 + 10;
int stack[N], top;
// 初始化 使用前一定记得初始化
void init()
{
top = 0; //stack[0]不存数据
}
// 压栈
void push(int x)
{
stack[++top] = x;
}
// 弹栈
void pop()
{
if(top==0)
{
return;
}
top--;
}
// 判空
bool empty()
{
return top == 0 ? true : false;
}
// 查询栈顶元素
int query()
{
return stack[top];
}
队列
#include<iostream>
using namespace std;
const int N = 1e5 + 7;
int m;
int q[N], h, t = -1;//h表示队头,t表示队尾
int main() {
cin >> m;
while (m -- ) {
string op;
int x;
cin >> op;
if (op == "push") {
cin >> x;
q[ ++ t] = x;
}
else if (op == "pop") h ++ ;
else if (op == "empty") cout << (h <= t ? "NO" : "YES") << endl; //队列不为空时有只有一个节点时t==h不止一个t>h
else cout << q[h] << endl;;
}
return 0;
}
链表
单向链表
const int N = 100010;
// head 表示头结点的下标
// e[i] 表示节点i的值
// en[i] 表示节点i的next指针是多少
// idx 存储当前已经用到了哪个点
int head, e[N], en[N], idx; //e[N]存数据 en[N]放指针/代表其位置
//初始化链表
void init()
{
head=-1;
idx=0;
}
///在链表头插入一个数a
void inserthead(int a)
{
e[idx]=a;
en[idx]=head;
head=idx;
idx++;
}
//将x插入在下标k的数后面
void insertlocalK(int k,int x)
{
e[idx]=x;
en[idx]=en[k];
en[k]=idx++;
}
//删除下标为k的数的后面一个节点
void deletlocalK(int k)
{
en[k]=en[en[k]];
}
双向链表
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int e[N], l[N], r[N], idx;
int n;
// 初始化
void init()
{
//用下标0表示链表的左端点,用下标1表示链表的右端点
r[0] = 1, l[1] = 0, idx = 2;
}
// 在下标为k的结点的右侧插入结点
void add(int k, int x)
{
e[idx] = x, r[idx] = r[k], l[idx] = k, l[r[k]] = idx, r[k] = idx, idx++;
}
// 删除下标为k的结点
void remove(int k)
{
r[l[k]] = r[k], l[r[k]] = l[k];
}
int main()
{
init();
scanf("%d", &n);
char op[5];
while (n--)
{
scanf("%s", op);
if (*op == 'L')
{
int x;
scanf("%d", &x);
add(0, x);
}
else if (*op == 'R')
{
int x;
scanf("%d", &x);
add(l[1], x);
}
else if (*op == 'D')
{
int k;
scanf("%d", &k);
remove(k + 1); // cur从2开始,若要移除第k个插入点,需传入k + 1
}
else if (!strcmp(op, "IL"))
{
int k, x;
scanf("%d%d", &k, &x);
add(l[k + 1], x);
}
else
{
int k, x;
scanf("%d%d", &k, &x);
add(k + 1, x);
}
}
for (int i = r[0]; i != 1; i = r[i]) printf("%d ", e[i]);
puts("");
return 0;
}
二、单调栈/单调队列(滑动窗口)
单调栈
单调栈中存放的数据应该是有序的,所以单调栈也分为单调递增栈和单调递减栈。利用这种单调性在解决一些问题时能有奇效;
例如:
//利用单调栈的特点解决查找一组数据里每个数据左边距离最近比其小的数将其输出,无则输出-1
//例:1 2 6 4 5
//输出:-1 1 2 2 4
对于一个新元素,越靠近栈顶的元素离新元素位置越近。所以不断比较新元素与栈顶,如果新元素比栈顶大,则可断定栈顶是新元素的答案,于是将新元素压入栈顶。如果新元素比栈顶小,持续出栈直到栈顶小于新元素,将其输出(打印),且在栈顶插入这个新元素,但如果栈在最后都空了就输出-1,过程中形成的栈一直都是单调递减的;
模拟实际情况的图
一些运用场景
视野和/最大区间/最大矩形
//cpp
//参考y总
//利用单调栈的特点解决查找一组数据里每个数据距离最近比其小的数,无则输出-1
//例如:1 2 3 4 5
//输出:-1 1 2 3 4
#include <iostream>
using namespace std;
const int N = 100010;
int stk[N], tt;
int main()
{
int n;
cin >> n;
while (n -- )
{
int x;
scanf("%d", &x);
while (tt && stk[tt] >= x) tt -- ;//如果栈顶元素大于当前待入栈元素,则出栈
if (!tt) printf("-1 ");//如果栈空,则没有比该元素小的值。
else printf("%d ", stk[tt]);//栈顶元素就是左侧第一个比它小的元素。
stk[ ++ tt] = x; //tt为0的位置不存放数据
}
return 0;
}
单调队列
实用教学
单调队列定义:
其实单调队列就是一种队列内的元素有单调性(单调递增或者单调递减)的队列,答案(也就是最优解)就存在队首,而队尾则是最后进队的元素。因为其单调性所以经常会被用来维护区间最值 或者 降低DP的维数已达到降维来减少空间及时间的目的。
单调队列可以有两个操作:
1、插入一个新的元素,该元素从队尾开始向队首进行搜索,找到合适的位置插入之,如果该位置原本有元素,则替换它。
2、在过程中从队首删除不符合当前要求的元素。
单调队列实现起来可简单,可复杂。简单的一个数组,一个head,一个tail指针就搞定。复杂的用双向链表实现。
单调队列的一般应用:
1.维护区间最值
2.优化DP
作者:一塌糊涂
链接:https://www.acwing.com/blog/content/32558/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。