栈(Stack)是一种常见的数据结构,遵循后进先出(LIFO)的原则。它类似于现实生活中的堆叠物品,最后放入的物品会最先被取出。接下来将逐一介绍栈的基本概念、核心操作和实现方式,并最后介绍单调栈。
栈的基本介绍
栈是一种线性数据结构,由一系列元素组成,它只允许在一端进行插入和删除操作。这一端通常称为栈顶。栈遵循后进先出的原则,即最后一个入栈的元素将会最先被出栈。栈通常具有压栈(push)、弹栈(pop)、取栈顶元素(top)和判空等基本操作。
栈的插入
栈的插入操作又称为压栈,将一个元素添加到栈顶。压栈操作需要保证栈顶指针指向插入的位置,并将元素放入该位置。
栈的删除
栈的删除操作又称为弹栈,从栈顶移除一个元素并返回它。弹栈操作需要保证栈顶指针指向正确的位置,并返回该位置的元素。
C++ STL 实现
在 C++ 标准模板库中,栈可以使用 <stack>
头文件中的 stack
模板类来实现。该模板类提供了压栈、弹栈、取栈顶元素、判空等核心操作,使用起来非常方便。
#include <stack>
std::stack<int> s; // 声明整型栈
s.push(1); // 压栈,将元素 1 添加到栈顶
s.pop(); // 弹栈,移除栈顶元素
int topElement = s.top(); // 获取栈顶元素的值
bool isEmpty = s.empty(); // 判断栈是否为空
C 语言手写实现
在 C 语言中,可以使用数组或链表手动实现栈。使用数组实现时,需要维护栈顶指针,插入和删除操作需要移动指针和数据。使用链表实现时,可以考虑使用头指针来表示栈顶。
单调栈的基本介绍
单调栈(Monotonic Stack)是栈的一种特殊应用,主要用于解决找下一个更大元素、找下一个更小元素等问题。单调栈不再遵循严格的后进先出原则,而是具有一定的单调性,通常是单调递增或单调递减。
单调栈的插入
单调栈的插入操作类似于普通栈,将一个元素添加到栈顶。不同之处在于,插入元素后需要调整栈内的元素,以保持单调性,通常是保持单调递增或单调递减。
单调栈的删除
单调栈的删除操作也类似于普通栈,从栈顶移除一个元素并返回它。同样需要根据单调性进行相应的调整。
C++ STL 实现单调栈
C++标准模板库(STL)中没有提供直接的单调栈实现,但我们可以利用std::stack
结合其他数据结构和算法来实现单调栈。以下是一个示例,以单调递增栈为例:
#include <stack>
#include <vector>
std::vector<int> findNextGreaterElements(std::vector<int>& nums) {
int n = nums.size();
std::vector<int> result(n, -1);
std::stack<int> monoStack; // 单调递增栈,存储元素的下标
for (int i = 0; i < n; i++) {
while (!monoStack.empty() && nums[i] > nums[monoStack.top()]) {
result[monoStack.top()] = nums[i]; // 当前元素是栈顶元素的下一个更大元素
monoStack.pop();
}
monoStack.push(i); // 将当前元素的下标压入栈中
}
return result;
}
在上述示例中,我们使用std::stack
实现了一个单调递增栈。我们遍历输入数组nums
,对于每个元素,如果它比栈顶元素大,则将栈顶元素的下一个更大元素更新为当前元素,并将栈顶元素弹出。最后,将当前元素的下标压入栈中。这样,我们就可以在result
数组中找到每个元素的下一个更大元素了。
C 语言手写实现单调栈
在 C 语言中,可以通过使用数组和相关的算法来手动实现单调栈。以下是一个示例,以单调递增栈为例:
#include <stdio.h>
#include <stdlib.h>
struct Stack {
int* array;
int size;
int top;
};
struct Stack* createStack(int maxSize) {
struct Stack* stack = (struct Stack*)malloc(sizeof(struct Stack));
stack->array = (int*)malloc(maxSize * sizeof(int));
stack->size = maxSize;
stack->top = -1;
return stack;
}
int isEmpty(struct Stack* stack) {
return stack->top == -1;
}
void push(struct Stack* stack, int num) {
stack->array[++stack->top] = num;
}
int pop(struct Stack* stack) {
if (isEmpty(stack)) {
printf("Stack is empty\n");
return -1;
}
return stack->array[stack->top--];
}
// 寻找nums数组中每个元素的下一个更大元素
int* findNextGreaterElements(int* nums, int numsSize) {
int* result = (int*)malloc(numsSize * sizeof(int));
struct Stack* monoStack = createStack(numsSize);
for (int i = 0; i < numsSize; i++) {
while (!isEmpty(monoStack) && nums[i] > nums[monoStack->array[monoStack->top]]) {
result[monoStack->array[monoStack->top]] = nums[i]; // 当前元素是栈顶元素的下一个更大元素
pop(monoStack);
}
push(monoStack, i); // 将当前元素的下标压入栈中
}
while (!isEmpty(monoStack)) {
result[monoStack->array[monoStack->top]] = -1; // 栈中剩余元素的下一个更大元素不存在
pop(monoStack);
}
free(monoStack->array);
free(monoStack);
return result;
}
在这个示例中,我们使用了一个自定义的Stack
结构体来实现单调递增栈。我们使用动态数组来存储栈元素,并利用常用的栈操作函数,如createStack
、isEmpty
、push
和pop
等。我们遍历输入数组nums
,对于每个元素,如果它比栈顶元素大,则将栈顶元素的下一个更大元素更新为当前元素,并将栈顶元素弹出。最后,将队列中剩余元素的下一个更大元素设置为-1。这样,我们就找到了每个元素的下一个更大元素。