【数据结构】栈(Stack)详解——数据结构的“后进先出”
在数据结构的世界里,栈是最基础、最常用的一种线性数据结构。它在很多计算机应用中都发挥着重要作用,比如浏览器的历史记录、函数调用的管理等等。今天,我们将带你一步步深入了解栈,掌握它的基本概念和常见操作,并通过示例代码演示栈的使用。
一、什么是栈?
栈(Stack)是一种线性数据结构,它遵循后进先出(LIFO)原则。
简单来说,栈就是一个“只能从一端进行操作的容器”,你只能从栈的顶部添加元素,也只能从栈的顶部移除元素。
🧩 栈的形象比喻
想象一下你有一摞书,每次你把书放到最上面,或者从最上面取书出来。
如果你从书堆顶上拿书,那么每次你拿到的都是最新放上去的那本书,而不是最底下的那本。
这种后进先出的特性,就是栈的核心。
二、栈的基本操作
栈有两个最基本的操作:
1️⃣ 入栈(Push)
将一个元素放到栈的顶部。
描述:将数据插入栈顶,栈顶指针上移。
2️⃣ 出栈(Pop)
将栈顶元素移除。
描述:删除栈顶元素,栈顶指针下移。
3️⃣ 栈顶元素(Peek / Top)
获取栈顶元素,但不移除它。
描述:返回栈顶元素的值,不改变栈的内容。
4️⃣ 栈空检查(isEmpty)
检查栈是否为空。
描述:如果栈为空,返回true;否则返回false。
三、栈的实现
栈的实现方式有很多,我们通常使用数组或链表来实现栈。在这里,我们首先使用数组来实现栈,因为数组的实现相对简单,而且大多数语言提供了内建的数组支持。
📝 栈的数组实现(C++示例)
#include <iostream>
using namespace std;#define MAX_SIZE 100 // 栈的最大容量class Stack {
private:int arr[MAX_SIZE]; // 用数组存储栈中的元素int top; // 栈顶指针public:Stack() { top = -1; } // 构造函数,初始化栈为空// 入栈操作void push(int value) {if (top == MAX_SIZE - 1) {cout << "栈已满,无法入栈!" << endl;return;}arr[++top] = value; // 栈顶指针上移并插入新元素cout << "入栈:" << value << endl;}// 出栈操作void pop() {if (top == -1) {cout << "栈为空,无法出栈!" << endl;return;}cout << "出栈:" << arr[top--] << endl; // 返回栈顶元素并栈顶指针下移}// 获取栈顶元素int peek() {if (top == -1) {cout << "栈为空!" << endl;return -1; // 返回一个非法值}return arr[top]; // 返回栈顶元素}// 检查栈是否为空bool isEmpty() {return top == -1;}// 打印栈的内容void print() {if (top == -1) {cout << "栈为空!" << endl;return;}cout << "栈内容:";for (int i = 0; i <= top; i++) {cout << arr[i] << " ";}cout << endl;}
};
💻 使用栈的演示(C++ 示例)
int main() {Stack s;// 入栈操作s.push(10);s.push(20);s.push(30);s.push(40);s.print(); // 输出栈的内容// 获取栈顶元素cout << "栈顶元素:" << s.peek() << endl;// 出栈操作s.pop();s.pop();s.print(); // 输出栈的内容// 检查栈是否为空if (s.isEmpty()) {cout << "栈为空" << endl;} else {cout << "栈不为空" << endl;}return 0;
}
🧑💻 代码输出:
入栈:10
入栈:20
入栈:30
入栈:40
栈内容:10 20 30 40
栈顶元素:40
出栈:40
出栈:30
栈内容:10 20
栈不为空
四、栈的应用场景
栈在计算机科学中有着非常广泛的应用。以下是一些典型的栈应用场景:
1️⃣ 函数调用管理(递归调用)
栈被广泛应用于函数调用的管理。当我们调用一个函数时,系统会把当前函数的信息(例如局部变量、返回地址等)压入栈中。函数执行完毕后,再从栈中弹出信息,返回到调用点。
2️⃣ 浏览器历史记录
浏览器通过栈来管理页面历史。当我们访问新页面时,浏览器将当前页面压入栈中,点击“后退”按钮时,就从栈中弹出一个页面,回到之前的页面。
3️⃣ 括号匹配
在编译器中,栈常用于检查表达式中的括号是否匹配。例如,对于一个字符串{[()()]},我们可以使用栈来判断括号是否成对出现,顺序是否正确。
4️⃣ 表达式求值
栈在中缀表达式转换为后缀表达式(逆波兰表达式)时常用到。计算机通过栈来存储操作符和操作数,从而实现高效的表达式求值。
五、栈的优缺点
| 优点 | 缺点 |
|---|---|
| 操作简单:栈只需要操作栈顶,入栈和出栈的时间复杂度为 O(1)。 | 空间限制:栈的空间固定,若栈满,则无法再入栈。 |
| 高效:栈非常适合用来管理临时数据,例如函数调用、回溯等。 | 只能从栈顶访问:不像数组,可以随机访问任意元素,栈只能操作栈顶元素。 |
| 简洁:栈的实现简单,代码清晰易懂。 | 不适合查找操作:栈是线性结构,查找特定元素的效率较低。 |
六、总结
栈是一种非常重要的线性数据结构,它遵循**后进先出(LIFO)**原则,在计算机的许多应用中都有着至关重要的作用。从函数调用、括号匹配到表达式求值,栈的应用遍及计算机的各个层面。
