好久不见,超级想念
废话不多说,直接看
引言
在数据结构的大家族中,栈(Stack)是一种非常重要的线性数据结构,它的特点是后进先出(LIFO,Last In First Out)。栈在程序设计中有着广泛的应用,比如函数调用栈、浏览器的前进后退功能等。本文将详细介绍栈的基本概念、操作以及使用C语言实现栈的方法。
栈的基本概念
栈是一种只能在一端(称为栈顶,top)进行插入和删除操作的线性表。在栈中,允许插入和删除的一端称为栈顶,另一端称为栈底(bottom)。栈没有后驱结点,插入和删除运算都只能在栈顶进行。
栈的实现
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的
代价比较小。
// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
STDataType _a[N];
int _top; // 栈顶
}Stack;
我只做部分讲解,不太难哈哈哈
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 定义栈的数据类型
typedef int STDataType;
// 定义栈的结构体
typedef struct Stack
{
STDataType* _a;
int _top; // 栈顶
int _capacity; // 容量
} Stack;
// 初始化栈
void StackInit(Stack* ps)
{
ps->_a = NULL;
ps->_top = -1;
ps->_capacity = 0;
}
// 扩容栈
void StackResize(Stack* ps, int newCapacity)
{
STDataType* temp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newCapacity);
if (!temp)
{
exit(-1); // 内存分配失败,退出程序
}
ps->_a = temp;
ps->_capacity = newCapacity;
}
在函数中,首先通过realloc函数重新分配内存,将栈的数据数组_a扩充到新的容量newCapacity。realloc函数的第一个参数是原有的数据数组_a,第二个参数是新的容量newCapacity乘以每个元素的大小。
接着,将realloc返回的新内存地址赋值给临时指针变量temp。如果realloc函数分配内存失败,temp将为NULL。在这种情况下,通过if语句检查temp是否为NULL,如果是,则调用exit(-1)函数退出程序,表示内存分配失败。
如果realloc函数成功分配了新的内存空间,将临时指针temp指向的内存地址赋值给栈结构体指针ps的数据数组_a,以此来更新栈的数据数组。
最后,将新的容量newCapacity赋值给栈结构体指针ps的_capacity,表示栈的容量已经扩充到新的值。这样,栈就成功扩容了。
// 入栈
void StackPush(Stack* ps, STDataType data)
{
if (ps->_top == ps->_capacity - 1) // 栈满,扩容
{
int newCapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
StackResize(ps, newCapacity);
}
ps->_a[++(ps->_top)] = data; // 先移动指针再赋值
}
在函数中,首先通过条件判断检查栈是否已满。判断条件为栈顶指针_top是否等于栈的容量_capacity减去1。如果栈已满,即栈顶指针指向了最后一个位置,则需要对栈进行扩容。
在扩容的部分,首先计算新的容量newCapacity。如果栈当前容量为0(即栈为空),则新容量设为4;否则新容量设为当前容量的两倍。接着调用StackResize函数,将栈结构体指针ps和新容量newCapacity作为参数,进行栈的扩容操作。
如果栈未满,或者扩容完成后,接着执行入栈操作。将要入栈的数据data存入栈的数据数组_a中,存储位置为栈顶指针_top的下一个位置,即++(ps->_top)。这里使用了前置递增运算符,先将栈顶指针_top加1,然后再将数据data存入该位置,表示先移动指针再赋值。
通过这段代码,实现了将数据入栈的功能,并在栈满时自动扩容,保证了栈的容量能够满足存储需求。
// 出栈
void StackPop(Stack* ps)
{
if (!StackEmpty(ps)) // 栈不为空
{
ps->_top--;
// 如果栈中元素过少,可以考虑缩容,这里省略
}
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
if (!StackEmpty(ps))
{
return ps->_a[ps->_top];
}
return -1; // 栈为空,返回-1或其他错误标识
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
return ps->_top + 1;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{
return ps->_top == -1;
}
// 销毁栈
void StackDestroy(Stack* ps)
{
free(ps->_a);
ps->_a = NULL;
ps->_top = -1;
ps->_capacity = 0;
}
int main()
{
Stack s;
StackInit(&s);
// 入栈操作
StackPush(&s, 1);
StackPush(&s, 2);
StackPush(&s, 3);
// 获取栈顶元素
printf("栈顶元素为:%d\n", StackTop(&s));
// 出栈操作
StackPop(&s);
printf("出栈后,栈顶元素为:%d\n", StackTop(&s));
// 获取栈中有效元素个数
printf("栈中有效元素个数为:%d\n", StackSize(&s));
// 销毁栈
StackDestroy(&s);
return 0;
}
例题
括号匹配
#define MAX_SIZE 10000
typedef struct {
char data[MAX_SIZE];
int top;
} Stack;
void initStack(Stack *stack) {
stack->top = -1;
}
bool isEmpty(Stack *stack) {
return stack->top == -1;
}
bool isFull(Stack *stack) {
return stack->top == MAX_SIZE - 1;
}
void push(Stack *stack, char c) {
if (isFull(stack)) {
printf("Stack overflow\n");
} else {
stack->data[++stack->top] = c;
}
}
char pop(Stack *stack) {
if (isEmpty(stack)) {
printf("Stack underflow\n");
return '\0';
} else {
return stack->data[stack->top--];
}
}
bool isValid(char *s) {
Stack stack;
initStack(&stack);
for (int i = 0; s[i] != '\0'; i++) {
if (s[i] == '(' || s[i] == '{' || s[i] == '[') {
push(&stack, s[i]);
} else {
if (isEmpty(&stack)) {
return false;
}
char left = pop(&stack);
if ((s[i] == ')' && left != '(') || (s[i] == '}' && left != '{') || (s[i] == ']' && left != '[')) {
return false;
}
}
}
return isEmpty(&stack);
}
这段代码定义了一个基于数组的栈结构,并实现了栈的基本操作(初始化、判断是否为空、判断是否已满、压栈、弹栈),以及一个isValid函数,用于检查一个给定的字符串s中的括号是否有效。
栈的定义和操作
- 栈的定义:使用了一个结构体Stack,其中包含一个字符数组data(用于存储栈元素)和一个整数top(用于表示栈顶的位置)。
- 初始化:initStack函数将栈顶指针top初始化为-1,表示栈为空。
- 判断是否为空:isEmpty函数检查top是否为-1。
- 判断是否已满:isFull函数检查top是否等于MAX_SIZE - 1。
- 压栈:push函数在栈未满时将字符c压入栈中。
- 弹栈:pop函数在栈非空时弹出栈顶元素,并返回该元素。
isValid函数分析
isValid函数用于检查一个字符串s中的括号是否有效。它使用前面定义的栈结构,并遵循以下逻辑:
- 遍历字符串s中的每个字符。
- 如果遇到左括号((、{ 或 [),则将其压入栈中。
- 如果遇到右括号()、} 或 ]),则执行以下步骤:
首先检查栈是否为空。如果为空,说明没有对应的左括号可以匹配,返回false。
然后弹出栈顶元素(即最近压入的左括号),检查它是否与当前的右括号匹配。如果不匹配,返回false。 - 遍历完字符串后,如果栈为空,则说明所有括号都有效匹配,返回true;否则,返回false。
注意事项
- 代码中使用的MAX_SIZE定义了栈的最大容量,但请注意,这个值在实际使用中可能需要调整,以确保足够大以处理可能的输入字符串。
- pop函数在栈空时返回\0,这只是一个表示错误的标记。在实际应用中,可能还需要考虑其他错误处理机制。
- 字符串s只包含括号字符,没有考虑其他字符(如字母、数字或空格)。如果输入字符串包含这些字符,isValid函数可能无法正确工作。
- isValid函数只考虑了三种括号类型(圆括号、大括号和方括号),并且假设它们的匹配是成对出现的。如果输入字符串包含其他类型的括号或括号不成对出现,函数将无法正确工作。
那么看到这里,这篇小文章就快结束了哦
不要忘了今天是母亲节,大家不要忘了问候自己的妈妈哦,嘻嘻嘻
那么各位,我们下期再见咯,今天没有预告哈哈哈