目录
前言
栈是一种特殊的线性表,只允许在固定的一端进行插入删除元素,我们可以使用顺序表实现栈,因为它的尾插尾删就代表了压栈出栈,极其方便。当然,我们也可使用链表实现栈,将单链表的头插头删作为压栈出栈,不要将单链表的尾部作为栈顶,这样就增大压栈出栈的难度。但如果要在两者之间选择,我们会选择数组实现,因为数组实现的栈会在缓冲区高效命中,而链表则命中效率低。
一、栈
1.1栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
后进先出
出栈并不需要将所有元素压栈之后再出栈,也可以边压栈边出栈
例:若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是(C)
A 1,4,3,2
B 2,3,4,1
C 3,1,4,2
D 3,4,2,1
二、各函数定义
栈的结构
- 数组a模拟栈
- top代表下标
- capacity代表此时数组容量,必要时进行扩大,实现动态数组
typedef int STDataType;
typedef struct Stack
{
STDataType*a;
STDataType top;
STDataType capacity;
}ST;
STInit 初始化
- 如果top是栈顶元素的下一个位置,那么数据放完++
- 如果top是栈顶元素,那么放数据时要先++再放数据
- top位置按习惯
void STInit(ST* ps)
{
assert(ps);
ps->a = (STDataType* )malloc(sizeof(STDataType) * 4);
if (ps->a == NULL)
{
perror("malloc fail");
return;
}
ps->capacity = 4;
ps->top = 0;//top是栈顶元素的下一个位置,数据放完++
//如果top是栈顶元素,那么放数据时要先++再放数据
//ps->top = -1; //top是栈顶元素
}
STDestroy 释放
- 数组一旦释放就全部释放
- 将 top 和 capacity 置空
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
STPush 压栈
- 判空
- 判断数组大小,进行扩大
- 数据压栈
void STPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->a = tmp;
ps->capacity *= 2;
}
ps->a[ps->top++] = x;//在top位置把数据放进去
}
STPop 出栈
- 实现数据出栈,直接将top-- 即可释放栈顶元素的访问权限,实现出栈功能
void STPop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));//检查是否为空,为空那么top就不能再减了
ps->top--;
}
STSize 计算元素个数
int STSize(ST* ps)
{
assert(ps);
return ps->top;//top是栈顶元素的下一个位置,正好对应栈内元素个数
}
STEmpty 判断是否为空
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;//top == 0为真就表示空
}
STTop 获得栈顶元素
STDataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));//元素为空就不能访问
return ps->a[ps->top - 1];//返回栈顶元素
}
三、数组实现栈
Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{
STDataType*a;
STDataType top;
STDataType capacity;
}ST;
void STInit(ST* ps);//初始化
void STDestroy(ST* ps);//释放
void STPush(ST* ps, STDataType x);//压栈
void STPop(ST* ps);//出栈
int STSize(ST* ps);//返回个数
bool STEmpty(ST* ps);//判空
STDataType STTop(ST* ps);
Stack.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
void STInit(ST* ps)
{
assert(ps);
ps->a = (STDataType* )malloc(sizeof(STDataType) * 4);
if (ps->a == NULL)
{
perror("malloc fail");
return;
}
ps->capacity = 4;
ps->top = 0;//top是栈顶元素的下一个位置,数据放完++
//如果top是栈顶元素,那么放数据时要先++再放数据
//ps->top = -1; //top是栈顶元素
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
void STPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->a = tmp;
ps->capacity *= 2;
}
ps->a[ps->top++] = x;//在top位置把数据放进去
}
void STPop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));//检查是否为空,为空那么top就不能再减了
ps->top--;
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;//top是栈顶元素的下一个位置,正好对应栈内元素个数
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;//top == 0为真就表示空
}
STDataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));//元素为空就不能访问
return ps->a[ps->top - 1];//返回栈顶元素
}
Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
int main()
{
ST st;
STInit(&st);
STPush(&st, 1);
STPush(&st, 2);
STPush(&st, 3);
STPush(&st, 4);
STPush(&st, 5);
while (!STEmpty(&st))
{
printf("%d ", STTop(&st));
STPop(&st);
}
return 0;
}
四、例题
1.括号匹配![](https://i-blog.csdnimg.cn/blog_migrate/70b52f377ec94a0b04557bbeabd05fc2.png)
采用栈的结构,将左括号入栈,右括号入栈时,去出栈顶元素进行匹配,若不匹配则返回false,若匹配则继续进行压栈匹配,直至循环结束,若此时栈内元素为空,则返回true,若栈内元素不为空,则返回false。
另外注意,再返回false之前要释放栈,避免内存泄露而引发大问题。
//栈
typedef char STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* ps)
{
assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (ps->a == NULL)
{
perror("malloc fail");
return;
}
ps->capacity = 4;
ps->top = 0;//top是栈顶元素的下一个位置,数据放完++
//如果top是栈顶元素,那么放数据时要先++再放数据
//ps->top = -1; //top是栈顶元素
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;//top == 0为真就表示空
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
void STPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->a = tmp;
ps->capacity *= 2;
}
ps->a[ps->top++] = x;//在top位置把数据放进去
}
void STPop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));//检查是否为空,为空那么top就不能再减了
ps->top--;
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;//top是栈顶元素的下一个位置,正好对应栈内元素个数
}
STDataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));//元素为空就不能访问
return ps->a[ps->top - 1];//返回栈顶元素
}
//1.左括号入栈
//2.右括号匹配
//程序
bool isValid(char* s)
{
ST st;
STInit(&st);
while (*s)
{
if (*s == '(' || *s == '[' || *s == '{')
{
STPush(&st,*s);
}
else
{
if (STEmpty(&st))//若为空,则返回false
{
STDestroy(&st);
return false;
}
char top = STTop(&st);//取出栈顶元素,进行 匹配
STPop(&st);//出栈
if (*s == ')' && top != '(')
{
STDestroy(&st);
return false;
}
else if (*s == '}' && top != '{')
{
STDestroy(&st);
return false;
}
else if (*s == ']' && top != '[')
{
STDestroy(&st);
return false;
}
}
s++;//s指向下一个字符
}
bool ret = STEmpty(&st);//判断栈内是否为空
STDestroy(&st);
return ret;
}
总结
栈的内容并不难,其模拟实现即可用数组(尾部做栈顶),也可用单向链表(头部做栈顶),在微小细节的把握是关键,主函数内初始化,释放,传参的参数...要注意
下一篇,我将讲解队列的实现,其结构与栈相似,希望小伙伴们在下一篇前掌握本节内容。最后,如果小帅的本文哪里有错误,还请大家指出,请在评论区留言(ps:抱大佬的腿),新手创作,实属不易,如果满意,还请给个免费的赞,三连也不是不可以(流口水幻想)嘿!那我们下期再见喽,拜拜!