今天我们来聊聊栈,栈的基本模型其实比较简单,他所拥有的特性也就是LIFO(后进先出),实现起来可以通过顺序表的基本结构来实现,也可以用链表的基本结构来实现。
可是既然已经有了顺序表和链表这种结构,那么栈存在的意义在哪里呢?这就是我们今天要聊的,首先压栈和弹栈这两项基本操作的时间复杂度都是O(1),其次,栈的后进先出这一特性决定了在很多应用场景下栈都可以有很大的发挥。
比如,我们在写程序的时候,有一种技巧叫做递归,递归从粗浅的层次看起来就像是自己调用自己,其实本质上并非如此简单,我们前面聊过栈帧,递归的每一次调用就是一次栈帧开辟的过程,如果递归的深度是10,那么就会开辟10个栈帧结构,在程序运行的过程中,这10个结构要一直维持,直到递归向上返回,而栈的特性在很大程度上与递归栈帧的开辟过程是极为相似的。把递归中第一个开辟的结构当做栈底,最后一个开辟的结构当做栈顶,弹栈就可以看作是递归的返回过程。
所以我们可以用栈结构来模拟实现递归的过程,这毫无疑问可以为我们省下一些开销,当然,我们也要为此付出一些代价,比如创建一个栈结构,当然,这都是值得的,特别是递归中的一大风险,栈溢出(这里的栈是指内存中的堆栈,是内存中一部分内存空间,而并非栈的数据结构),我们使用栈结构就可以完全规避。
下来提供一份代码,仅供大家参考,其中测试部分写了一个测试括号是否匹配的样例,大家可以自行理解栈的应用:
头文件(.h)
#pragma once
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
#include<string.h>
#define Stack_Init_Deep 100
typedef int Datatype;
typedef struct Stack{
Datatype* _Elem; //数据块指针
int _top; //栈顶
int _capacity; //容量
}Stack,*PStack;
//初始化栈
void StackInit(PStack s);
//压栈
void StackPush(PStack s, Datatype data);
//弹栈
void StackPop(PStack s);
//动态增容
void StackAddCapacity(PStack s);
//获取栈中元素个数
int StackSize(PStack s);
//返回栈顶元素
Datatype StackData(PStack s);
// 检测栈是否为空
int StackEmpty(PStack s);
//清空栈内元素
void StackClear(PStack s);
//销毁栈
void StackDestroy(PStack s);
//判断括号是否匹配
int JudgeBracket(PStack s, char str[]);
功能部分:
#include"Apr_24.h"
//初始化栈
void StackInit(PStack s)
{
s->_Elem = (Datatype*)malloc(Stack_Init_Deep * sizeof(Datatype));
if (NULL == s->_Elem)
{
printf("申请失败\n");
}
s->_top = 0;
s->_capacity = Stack_Init_Deep;
}
//压栈
void StackPush(PStack s, Datatype data)
{
assert(s);
if (s->_capacity == s->_top)
{
StackAddCapacity(s);
}
*(s->_Elem + s->_top) = data;
++s->_top;
}
//弹栈
void StackPop(PStack s)
{
assert(s);
if (0 == s->_top)
{
printf("栈已空\n");
return;
}
--s->_top;
}
//返回栈顶元素
Datatype StackData(PStack s)
{
assert(s);
return *(s->_Elem + s->_top - 1);
}
//获取栈中元素个数
int StackSize(PStack s)
{
assert(s);
return s->_top;
}
// 检测栈是否为空
int StackEmpty(PStack s)
{
assert(s);
if (s->_top == 0)
return 0;
return 1;
}
//动态增容
void StackAddCapacity(PStack s)
{
assert(s);
Datatype* newbase = (Datatype*)realloc(s->_Elem, s->_capacity * 2 * sizeof(Datatype));
if (NULL == newbase)
{
printf("增容失败\n");
}
s->_capacity = s->_capacity * 2;
s->_Elem = newbase;
}
//清空栈内元素
void StackClear(PStack s)
{
assert(s);
s->_top = 0;
}
//销毁栈
void StackDestroy(PStack s)
{
assert(s);
StackClear(s);
free(s->_Elem);
s->_capacity = 0;
s->_Elem = NULL;
}
//判断括号是否匹配
int JudgeBracket(PStack s,char str[])
{
assert(s);
int len = strlen(str);
int i = 0;
for (; i < len; i++)
{
if (str[i] == '(' ||
str[i] == '[' ||
str[i] == '{' ||
str[i] == ')' ||
str[i] == ']' ||
str[i] == '}')
{
if (str[i] == '(' ||
str[i] == '[' ||
str[i] == '{')
StackPush(s, str[i]);
if(StackEmpty(s))
{
if ((str[i] == ')' && StackData(s) == '(') ||
(str[i] == ']' && StackData(s) == '[') ||
(str[i] == '}'&& StackData(s) == '{'))
{
StackPop(s);
}
else
{
continue;
}
}
if (StackEmpty(s) == 0 && i == len - 1)
{
return 1;
}
}
}
return 0;
}
测试部分
#define _CRT_SECURE_NO_WARNINGS 1
#include"Apr_24.h"
void test()
{
Stack s;
char str[] = "(())abc{[([)]]}";
StackInit(&s);
printf("%d\n", s._capacity);
printf("%d\n", StackSize(&s));
printf("%d\n", JudgeBracket(&s, str));
StackDestroy(&s);
}
int main()
{
test();
return 0;
}