栈和队列

$$\texttt{栈}$$

栈(stack),一种后进先出(last in first out,LIFO)的数据结构,主要有三种操作:压入一个元素到栈顶(push(x)),弹出栈顶的元素(pop()),访问栈顶元素(top()),当然也有询问大小(size())和返回是否为空(empty())。

注:后进先出指的是在当前容器内后进来的先出。

最初的最初:实现一个基本的栈

OI-wiki Link

由于三个操作都只与栈顶有关,所以可以用数组轻松实现。

int stk[N], top;

void Pop () { // 弹出栈顶
  top--;
}

void Push (int x) { // 将元素压入栈
  stk[++top] = x;
}

int Top () { // 返回栈顶元素
  return stk[top];
}

int Size () { // 返回栈的大小
  return top;
}

bool Empty () { // 如果栈为空返回 1,否则返回 0
  return !top;
}

同时 C with STL(c++) 也附赠了一个 STL 容器 stack<元素类型> 容器名;

需要头文件 #include <stack>

它的成员函数如下:stack<int> stk;

  • 元素访问
    • stk.top() 返回栈顶。
  • 修改
    • stk.push(x)\(x\) 压入栈顶。
    • stk.pop() 弹出栈顶。
  • 容量
    • stk.empty() 返回是否为空。
    • stk.size() 返回元素数量。

废柴的进阶:双端栈

顾名思义,等同于双端队列,见下文。

一个有用的进阶:单调栈

OI-wiki Link

单调栈,顾名思义,就是满足单调性的栈。

假设现在有一个存储整数的单调栈,满足从栈顶往下数都是单调递增的。

初始状态:\([0,11,45,81]\)

如果要压入整数 \(14\),为了保证单调性,要将两个比 \(14\) 小的数弹出,再压入整数 \(14\),此时栈中元素为 \([14,45,81]\)

const int N = 2e5 + 10;

int stk[N], top;

void Pop () { // 弹出栈顶
  top--;
}

void Push (int x) { // 将元素压入栈
  while (top && stk[top] <= x) {
    Pop();
  }
  stk[++top] = x;
}

int Top () { // 返回栈顶元素
  return stk[top];
}

int Size () { // 返回栈的大小
  return top;
}

bool Empty () { // 如果栈为空返回 1,否则返回 0
  return !top;
}

由于每个元素最多只会入栈出栈各一次,所以复杂度为 \(O(n)\) (\(n\) 为元素数量)。

习题

$$\texttt{队列}$$

队列(queue),一种先进先出(first in first out,FIFO)的数据结构,它满足先入队列的元素一定先出队列(双端队列除外)。

最初的最初:实现一个基本的队列

OI-wiki Link

主要是四种操作:压入一个元素到队尾(push(x)),弹出队头的元素(pop()),访问队头元素(front()),访问队尾元素(back()),当然也有询问大小(size())和返回是否为空(empty())。

一般不建议手写,比较容易出锅,比起来 STL 真是个好东西,STL 队列 queue<元素类型> 容器名;

需要头文件 #include <queue>

它的成员函数如下:queue<int> q;

  • 元素访问
    • q.front() 返回队头元素。
    • q.back() 返回队尾元素。
  • 修改
    • q.push_back(x)\(x\) 压入队尾。
    • q.pop_front() 弹出队头。
  • 容量
    • q.empty() 返回是否为空。
    • q.size() 返回元素数量。

一个进阶:双端队列

OI-wiki Link

比起普通的队列,双端队列将 \(2\) 种压入弹出升级为了 \(4\) 种!

STL 也提供了双端队列,头文件没变。

deque<元素类型> 容器名;

假设现在 deque<int> dq;,那么四种压入弹出分别为:

  • dq.push_front(x)\(x\) 压入队头。
  • dq.push_back(x)\(x\)​ 压入队尾。
  • dq.pop_front() 弹出队头。
  • dq.pop_back() 弹出队尾。

别的成员函数没变,值得一提的是它支持随机化访问!即你可以用 dq[0] 来访问队头。

不过常数比 queue 大,非必要情况下不建议使用。

另一个进阶:单调队列

OI-wiki Link

先放一道例题:P1886 滑动窗口 /【模板】单调队列

大致题意就是给定一个长度为 \(n\) 的整数数组 \(a\),要对于每个长度为 \(k\) 的区间求出区间的最大最小值。

从前往后处理,为了维持单调性,那么当你要压入元素时,你可以用类似单调栈的方式,将那些不优的元素先弹出,再压入元素。

不同的是,这里区间长度为 \(k\),那么“过期”元素则需要特殊处理。很明显单调队列中的元素下标也是单调递增的,直接重复弹出队头即可。

由于涉及到了队尾弹出,可以使用双端队列。

细节有些,但不多。

习题

posted @ 2023-09-28 16:45  wnsyou  阅读(8)  评论(0编辑  收藏  举报