前言:
前篇学习了 数据结构的顺序表和单链表 那么这篇继续学习后面栈部分的基础内容。
/知识点汇总/
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作。
进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
即遵循“先进后出(LIFO:Last in First Out)”
压栈操作:栈的插入操作叫做栈/压栈/入栈,入数据在栈顶。
出栈操作:栈的删除操作叫做出栈,出数据也在栈顶。
基本分为两种形式:
数组栈:数组栈是以数组形式存储的栈;(最佳实现);
链式栈:而链式栈是以链式结构形式存储的栈;
双向链表实现:栈顶可以是尾,也可以是头;
单链表:栈顶只能是头。
数组栈的实现的结构类型,那么就需要类似于顺序表一样的存储结构,不过额外注意一个top表示栈顶,如下所示:
typedef int STDatatype;
//定义数组栈结构体成员及变量
typedef struct Stack
{
STDatatype* a;
int top; //标识栈顶位置
int capacity;
}ST;
//栈初始化
void STInit(ST* pst);
//栈销毁
void STDestory(ST* pst);
//入栈
void STPush(ST* pst, STDatatype x);
//出栈
void STPop(ST* pst);
//获取栈顶元素
STDatatype STTop(ST* pst);
//栈空判定
bool STEmpty(ST* pst);
//获取栈的大小
int STSize(ST* pst);
首先对于栈的初始化中,需要确定对于栈顶的初始化为0还是-1,因为要明确top是栈顶元素还是栈顶元素的下一个元素。而且此时栈内只有一个元素时表示top=0,那么top无法区分top指向栈顶元素,还是指向栈顶元素的下一个位置,所以给-1还是0,写法不同。
//栈初始化
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->capacity = 0;
//pst->top = -1; //表示top指向栈顶元素的位置
pst->top = 0; //表示top指向栈顶元素的下一个元素位置。
//主要区别,先++top还是先赋值的区别,根据自己合理即可
}
只需要正确的调用使用free函数即可。
//栈销毁
void STDestory(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->capacity = pst->top = 0;
}
由于栈是特殊的线性表,只能在一端进行数据的操作,具体就是把数据放入top的位置,然后移动top栈顶指针即可。同理,出栈就是移除元素top指针下移。
//入栈
void STPush(ST* pst, STDatatype x)
{
assert(pst);
//检查容量,扩容
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDatatype* tmp = (STDatatype*)realloc(pst->a, sizeof(STDatatype) * newcapacity);
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(pst->top > 0);
pst->top--;
}
取栈顶元素的操作就是根据初始化时定义的top情况,输出该数组栈顶元素即可。
//获取栈顶元素
STDatatype STTop(ST* pst)
{
assert(pst);
//判空
assert(pst->top > 0);
return pst->a[pst->top - 1];//注意此时top表示栈顶元素的下一个,所以需要减1
}
对于栈顶可以判定栈为空的情况。
//栈空判定
bool STEmpty(ST* pst)
{
assert(pst);
//直接以判断返回即可
return pst->top == 0;
}
想要得到栈的大小, 直接返回top的数值即可。
//获取栈的大小
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
#include "Stack.h"
//测试1:
void TestST1()
{
ST s;
STInit(&s);//初始化
STPush(&s, 1);//入栈
STPush(&s, 2);
STPush(&s, 3);
STPush(&s, 4);
STPush(&s, 5);
while (!STEmpty(&s))//判空循环
{
printf("%d ", STTop(&s));//取栈顶元素
STPop(&s);//出栈
}
printf("\n");
}
//测试2:不同出栈顺序
void TestST2()
{
ST s;
STInit(&s);//初始化
STPush(&s, 1);//入栈
STPush(&s, 2);
STPush(&s, 3);
printf("%d ", STTop(&s));//取栈顶元素
STPop(&s);//出栈
STPush(&s, 4);
STPush(&s, 5);
while (!STEmpty(&s))//判空循环
{
printf("%d ", STTop(&s));//取栈顶元素
STPop(&s);//出栈
}
printf("\n");
}
int main()
{
//TestST1();
TestST2();
return 0;
}
小结:
栈的性质:先进后出,但是入栈顺序相同的情况下,出栈顺序不一定相同。所以出入栈顺序是相对的,一对多关系。
有效的括号 – ‘(’ ,‘)’,‘[’,‘]’,‘{’.'}'的匹配
思路:数量匹配,顺序匹配
左括号入栈,有右括号则取栈顶左括号。
#include "Stack.h"
bool isValid(char* s)
{
ST st;
STInit(&st);//初始化
while (*s)
{
if (*s == '[' || *s == '(' || *s == '{')//如果是左括号就入栈
{
STPush(&st, *s);
}
else//否则遇见右括号就出栈
{
//数量匹配问题,右括号比左括号多的情况
if (STEmpty(&st))
{
STDestory(&st);//添加这一步,是因为这些特殊情况时,不会走到最后一步就返回了,导致没有释放内存,而导致内存泄漏。
return false;
}
//栈出左括号
char top = STTop(&st);
STPop(&st);
//顺序不匹配
if ((*s == ']' && top != '[')||
(*s == '}' && top != '{')||
(* s == ')' && top != '('))
{
STDestory(&st);
return false;
}
}
++s;
}
//栈为空,返回真,说明数量匹配,没有剩余括号
bool ret = STEmpty(&st);
STDestory(&st);
return ret;
}
int main()
{
char* ch = "{[({})]}";
if (isValid(ch))
{
printf("True\n");
}
else
{
printf("False\n");
}
return 0;
}