栈(Stack)是一种数据结构,具有后进先出(Last-In-First-Out, LIFO)的特性,即最后进入栈的元素最先退出栈,而最先进入栈的元素最后退出栈。如同堆叠的盘子,必须先放最下面的才能放上面的。栈是一种线性结构,在程序中广泛应用,是深入学习数据结构算法的基础。
栈的概念有很多衍生,其中最常用的为顺序栈和链栈。顺序栈即是用一维数组来实现,链栈则是用链表来实现。栈的操作包括push(入栈)和pop(出栈)两种基本操作。
栈的性质有以下几点:
1. 栈的空间是连续的,空间有限,所以不能溢出;
2. 栈的插入和删除操作只针对栈顶,时间复杂度为O(1);
3. 栈可以用来解决后缀表达式和递归调用等问题;
4. 栈具有回溯功能,可以实现程序的撤销操作;
5. 栈的容量在程序运行前需要确定,运行过程中一般不改变。
如下是顺序栈的数据结构实现:
```c
#define MAXSIZE 100 // 定义栈的最大容量
typedef struct {
int data[MAXSIZE]; // 存储元素
int top; // 栈顶指针,-1为空栈
} stack;
// 初始化栈,将栈顶指针设为-1
void initStack(stack *s)
{
s->top = -1;
}
// 判断栈是否为空
int isEmpty(stack *s)
{
if (s->top == -1) {
return 1;
} else {
return 0;
}
}
// 判断栈是否已满
int isFull(stack *s)
{
if (s->top == MAXSIZE - 1) {
return 1;
} else {
return 0;
}
}
// 入栈操作
int push(stack *s, int x)
{
if (isFull(s)) {
return 0; // 栈已满,插入失败
} else {
s->top++; // 栈顶指针+1
s->data[s->top] = x; // 将元素x存入栈顶
return 1; // 插入成功
}
}
// 出栈操作
int pop(stack *s, int *x)
{
if (isEmpty(s)) {
return 0; // 栈为空,弹出失败
} else {
*x = s->data[s->top]; // 取出栈顶元素
s->top--; // 栈顶指针-1
return 1; // 弹出成功
}
}
```
除了顺序栈,链栈也是一种常用的数据结构实现方式。链栈则使用链表实现,需要自定义一个节点结构体来存储元素和下一个节点地址。链栈的操作和顺序栈类似,不过因为使用指针对节点进行操作,所以时间复杂度相对会高一些。
除了栈的基本操作,还有一些扩展操作,例如:
1. 获取栈顶元素:使用s->data[s->top]可以获得栈顶元素,也可以写一个函数来实现:
```c
int getTop(stack *s, int *x)
{
if (isEmpty(s)) {
return 0; // 栈为空,获取失败
} else {
*x = s->data[s->top]; // 取出栈顶元素
return 1; // 获取成功
}
}
```
2. 清空栈:使用initStack函数可以将栈的顶部指针设置为-1,相当于清空栈。
3. 获取栈的长度:使用s->top + 1可以获得栈的长度,也可以写一个函数来实现:
```c
int getLength(stack *s)
{
return s->top + 1;
}
```
栈的应用范围非常广泛,如括号匹配、数组元素逆置、迷宫求解、表达式求值等。在括号匹配中,可以使用栈来判断字符串中的括号是否匹配。在迷宫求解中,可以使用栈来记录行进路径,回溯时也可以从栈中取出遍历过的路径。在表达式求值中,可以利用栈来实现中缀表达式转后缀表达式,并计算出结果。
总之,栈是一种非常重要、常用、实用的数据结构,在程序中广泛应用。掌握栈的基础概念和性质是深入学习数据结构的基础。
微信扫一扫,领取最新备考资料