目录
一、栈的概念及结构
二、栈的实现
1、声明栈结构体
2、初始化
3、 销毁
4、 入栈(压栈)
5、出栈(弹栈)
6、栈的大小
OJ练习
完整版:
Stack.h声明
Stack.c函数
test.c参考测试用例
一、栈的概念及结构
栈(Stack)是一种常见的数据结构,它遵循一种特定的数据存储和访问方式,通常用于管理数据的后进先出(Last-In-First-Out,LIFO)的操作顺序。这意味着最后压入栈的元素将首先被弹出,类似于一叠盘子,你总是在最上面放入和拿出盘子。
栈通常支持以下两种基本操作:
- 压栈(Push):将元素添加到栈的顶部。这个操作通常叫做“入栈”。
- 弹栈(Pop):从栈的顶部移除元素。这个操作通常叫做“出栈”。
二、栈的实现
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小,(CPU高速缓存命中率会更高)
1、声明栈结构体
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
- 将栈的数据类型设置别名STDataType,方便后续修改。
- 定义结构体名称的别名为ST。
- 结构体成员*a用于指向存储栈元素的动态数组。
- top为栈顶的位置。
- capacity为当前栈的容量。
2、初始化
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
- assert判断栈的地址是否为空,为空则报错。
- 将指针a初始化为空,栈顶位置top初始化为0,容量capacity初始化为0。
3、 销毁
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
- assert判断栈的地址是否为空,为空则报错。
- free释放结构体成员a的空间,以避免内存泄漏,然后将a置空。
- 将栈顶位置top赋值为0,表示栈已空。
- 将容量capacity赋值为0,表示容量为0.
4、 入栈(压栈)
void STPush(ST* pst,STDataType x)
{
if (pst->top == pst->capacity) {
int newCapacity = pst->capacity == 0 ? 4 :pst-> capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
if (tmp == NULL) {
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newCapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
- 每次数据入栈都先检查栈的容量,top与capacity相等分为两种情况:第一次入栈或栈已满
- 第一次入栈时,新栈newCapacity的容量为4,然后为其开辟相应大小的空间,
- 如果已满即top与capacity相等,则新栈newCapacity的大小为当前的两倍,然后对当前空间进行扩容。
- 用中间变量接收扩容的空间,防止扩容失败丢失数据。
- 如果扩容失败则调用perror打印错误信息。
- 将中间变量赋值给 a。
- 更新容量为newCapacity的值。
- 将接收的参数 x 入栈到top位置,然后更新top位置向上移动一位。
5、出栈(弹栈)
出栈需要三个函数,
STPop
用于弹出元素,STTop
用于获取栈顶元素的值,而STEmpty
用于检查栈是否为空。
void STPop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
STDataType STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
void STPop(ST* pst)
函数:
- 这个函数用于弹出栈顶元素,也就是将栈顶位置
pst->top
的值减一,相当于将栈顶元素从栈中移除。assert(pst)
确保传入的pst
指针不为空。assert(!STEmpty(pst))
确保栈不为空,因为如果栈已经为空,就不应该执行弹出操作,否则会导致错误。pst->top--;
将栈顶位置减一,以实现弹出操作。
STDataType STTop(ST* pst)
函数:
- 这个函数用于获取栈顶元素的值,但不将其从栈中移除。
assert(pst)
确保传入的pst
指针不为空。assert(!STEmpty(pst))
确保栈不为空,因为如果栈为空,就不能安全地访问栈顶元素。return pst->a[pst->top - 1];
返回栈顶位置pst->top
上的元素值,由于栈是从0开始索引的,所以需要减一。
bool STEmpty(ST* pst)
函数:
- 这个函数用于检查栈是否为空。
assert(pst)
确保传入的pst
指针不为空。return pst->top == 0;
返回一个布尔值,表示栈是否为空。如果栈顶位置pst->top
等于0,说明栈为空,返回true
;否则返回false
。
6、栈的大小
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
- assert判断栈的地址是否为空,为空则报错。
- 返回栈的大小,即栈顶位置
pst->top
的值。栈顶位置表示栈中元素的数量,因为栈是从0开始索引的,所以pst->top
的值等于栈中元素的数量。
OJ练习
20. 有效的括号
由于我们用C语言做这道题,所以代码前要加上咱们实现的栈的代码,同时要将数据类型STDataType改为char类型。
typedef char STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
void STPush(ST* pst,STDataType x)
{
if (pst->top == pst->capacity) {
int newCapacity = pst->capacity == 0 ? 4 :pst-> capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
if (tmp == NULL) {
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newCapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
void STPop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
STDataType STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
bool isValid(char* s) {
ST st;
STInit(&st);
while (*s) {
if (*s == '(' || *s == '[' || *s == '{') {
STPush(&st, *s);
}
else {
if (STEmpty(&st)) {
STDestroy(&st);
return false;
}
char top = STTop(&st);
STPop(&st);
if ((top != '(' && *s == ')') ||
(top != '{' && *s == '}') ||
(top != '[' && *s == ']')) {
STDestroy(&st);
return false;
}
}
s++;
}
bool ret = STEmpty(&st);
STDestroy(&st);
return ret;
}
- 创建栈结构体ST变量 st,然后进行初始化。
- 以*s为循环进行条件
-
首先,创建一个名为
st
的ST
结构体实例,并使用STInit
初始化它。 - 然后,遍历输入字符串
s
中的每个字符。 - 对于每个字符,如果是左括号 '(','[','{' ,则将其推入栈中。
- 如果是右括号 ')',']','}' ,则执行以下操作:
检查栈是否为空,如果为空,表示没有对应的左括号,则销毁栈,返回
false
。否则,弹出栈顶元素,将其与当前右括号进行匹配。如果不匹配,则销毁栈,返回
false
。
- 最后,遍历完整个字符串后,检查栈是否为空。如果栈为空,表示所有括号都成功匹配,返回
true
,否则返回false
。 - 最后,调用
STDestroy
销毁栈,并返回最终的匹配结果。
完整版:
Stack.h声明
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);
Stack.c函数
#include "Stack.h"
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
void STPush(ST* pst,STDataType x)
{
if (pst->top == pst->capacity) {
int newCapacity = pst->capacity == 0 ? 4 :pst-> capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
if (tmp == NULL) {
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newCapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
void STPop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
STDataType STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
test.c参考测试用例
#include "Stack.h"
void test1()
{
ST st;
STInit(&st);
STPush(&st, 999);
STPush(&st, 99);
STPush(&st, 9);
//printf("%d ", STTop(&st));
//STPop(&st);
//printf("%d ", STTop(&st));
while (!STEmpty(&st)) {
printf("%d ", STTop(&st));
STPop(&st);
}
STDestroy(&st);
}
int main()
{
test1();
return 0;
}