目录
栈是什么?
通俗来说:
特殊线性表,只允许在固定一端进行插入和删除元素,进行数据插入和删除操作的一端叫栈顶,另一端叫栈底,栈相比于链表,除了存储展示修改数据,数据还满足后进先出(后面进来的先出去)
主要实现两种操作:
压栈:栈的插入操作,入数据在栈顶
出栈:栈的删除,出数据也在栈顶
例如:
入栈 顺序1 2 3 4
出栈顺序 有4321 1234(可以入一个出一个,1入了1又出了) 3241.....出的顺序很多
但也有不可能的顺序
实现栈有两种方式:
1.数组栈
除了扩容的缺点,你在栈顶出数据其实是非常方便的能够直接访问到,并且cpu高速缓存效率更高
2.链式栈
如果使用单链表,首先尾插尾删不方便,要不然得反过来搞,头做栈顶,尾做栈底,入数据是入栈就是头插,出数据就是出栈就是头删也比较方便
数组栈的实现:stack
首先要定义结构体(定不定义根据需求,有多个数据就要定义结构体)
需要数据类型指针a(数组栈与顺序表类似,用于开辟动态内存空间)
int top(标识栈顶位置就不叫size了,但与size的功能类似)
int capacity(记录容量)
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
typedef int STDataType;
//利用数组定义栈,相当于顺序表的方式
typedef struct STack {
int capacity;//控制容量方便realloc
int top;//类似于顺序表中size的作用记录有效数据
STDataType* p;//用于动态开辟内存空间
}ST;
void STackInit(ST*ps);//初始化
void STackPop(ST*ps);//出栈
void STackPush(ST*ps,STDataType x);//入栈
STDataType STTop(ST*ps);//获取栈顶元素
bool STEmpty(ST* ps);//判空函数,检查是空栈的情况
void STackDestroy(ST* ps);//释放栈
int STSize(ST* ps);//获取size
1.STInit初始化处理
void STackInit(ST* ps) {
assert(ps);//注意null情况
ps->p = NULL;
ps->capacity =0;
ps->top = 0;
}
//如果判为null不能直接malloc,因为我们通过一级指针访问结构体中的成员,如果想要改变ps(因为结构体指针现在为空),那么就应该传址,用二级指针实现
//top类似于size作用,如果设为0那么它将指向的是栈顶数据的下一个位置,如果是-1则指向与栈顶数据本身(从栈为空的角度看,top给0,先插入数据,然后top++,那么top就指向栈顶元素下一个),否则top指向4就要给-1,就是指向栈顶数据)
2.STPush入栈
void STackPush(ST* ps, STDataType x) {
assert(ps);
//让capacity==top时则需要扩容
if (ps->capacity == ps->top) {
int newcapacity = 0;//扩展新空间
if (ps->capacity == 0) { //原始空间为0
newcapacity == 4;
}
else {
newcapacity = (ps->capacity) * 4;
} //计算内存占空间的大小
STDataType* tmp = (STDataType*)realloc(ps->p,newcapacity*sizeof(STDataType));
if (tmp == NULL) {
perror("realloc error!");
}
ps->p = tmp;
ps->capacity = newcapacity;
}
ps->p[ps->top] = x;
ps->top++;
}
注意:扩容有异地扩容和原地扩容
创建新容量int newcapacity
把新容量的地址tmp返回值如果和a相同就是原地扩容,否则就是异地扩容
realloc,如果是空指针其实和malloc实现一样
3.STPop出栈
void STackPop(ST* ps) {
assert(ps);
assert(!STEmpty(ps));//断言如果为空栈就不存在删,不用传地址因为不需要改变它
ps->top--;
}
4.bool STEmpty判空(如果是静态的需要判满)
bool STEmpty(ST* ps) {//如果是空栈的情况
assert(ps);//首先ps不能为空
if (ps->top==0) {
return true;
}
return false;
}
5.int STSize获取size
int STSize(ST* ps) {
return ps->top;//因为top是0,如果top是-1的话就要+1
}
6.stdatatype STTop获取栈顶元素
STDataType STTop(ST* ps) {
assert(ps);
assert(!STEmpty(ps));//如果为空不能访问否则越界了
return ps->p[ps->top-1];
}
7.STDestroy栈的释放
void STackDestroy(ST* ps) {
assert(ps);
free(ps->p);//数组栈是连续的,给个头就全部释放掉了
ps->p == NULL;
ps->capacity = 0;
ps->top = 0;
}
注意:
栈的访问和其他线性表不同,不能随便访问,所以要一直去访问栈顶元素然后将栈顶元素后弹出,出一个访问一个,栈访问完了栈也空了,这样才能体现出数组栈和顺序表的不同之处
例题:
这道题就是典型的可以利用栈来实现:思路如下:
当我们遇到左括号就让其入栈,如果遇到右括号,就获取栈顶元素与之进行比较,因为栈顶元素其实就是前一个元素,那么可以进行匹配
注意:1.是否可以用成对的数目来判断,我认为是 不可以的因为存在数目对但不匹配的现象比如“((){)}”
2.如果给了只有一个“)”那么我们的栈为空如果访问的话会出问题,所以也存在到最后栈上留下一个右括号的情况,它出不去那么最后我们就需要加入一个判空步骤
代码如下:
typedef char STDataType;
//利用数组定义栈,相当于顺序表的方式
typedef struct STack {
int capacity;//控制容量方便realloc
int top;//类似于顺序表中size的作用记录有效数据
STDataType* p;//用于动态开辟内存空间
}ST;
void STackInit(ST*ps);//初始化
void STackPop(ST*ps);//出栈
void STackPush(ST*ps,STDataType x);//入栈
STDataType STTop(ST*ps);//获取栈顶元素
bool STEmpty(ST* ps);//判空函数,检查是空栈的情况
void STackDestroy(ST* ps);//释放栈
int STSize(ST* ps);//获取size
//用栈来实现
void STackInit(ST*ps) {
assert(ps);//如果判为null不能直接malloc,因为我们通过一级指针访问结构体中的成员,如果想要改变ps(因为结构体指针现在为空),那么就应该传址,用二级指针实现
ps->p = NULL;
ps->capacity =0;
ps->top = 0;//top类似于size作用,如果设为0那么它将指向的是栈顶数据的下一个位置,如果是-1则指向与栈顶数据本身
}
bool STEmpty(ST* ps) {//如果是空栈的情况
assert(ps);//首先ps不能为空
if (ps->top==0) {
return true;
}
return false;
}
void STackPush(ST* ps, STDataType x) {
assert(ps);
//让capacity==top时则需要扩容
if (ps->capacity == ps->top) {
int newcapacity = 0;
//扩展新空间
if (ps->capacity == 0) { //原始空间为0
newcapacity = 4;
}
else {
newcapacity = (ps->capacity) * 4;
} //计算内存占空间的大小
STDataType* tmp = (STDataType*)realloc(ps->p,newcapacity*sizeof(STDataType));
if (tmp == NULL) {
perror("realloc error!");
}
ps->p = tmp;
ps->capacity = newcapacity;
}
ps->p[ps->top] = x;
ps->top++;
}
void STackPop(ST* ps) {
assert(ps);
assert(!STEmpty(ps));//断言如果为空栈就不存在删,不用传地址因为不需要改变它
ps->top--;
}
STDataType STTop(ST* ps) {
assert(ps);
assert(!STEmpty(ps));///嗷嗷嗷不要忘记判空
return ps->p[ps->top-1];
}
void STackDestroy(ST* ps) {
assert(ps);
free(ps->p);//数组栈是连续的,给个头就全部释放掉了
ps->p == NULL;
ps->capacity = 0;
ps->top = 0;
}
int STSize(ST* ps) {
return ps->top;//因为top是0,如果top是-1的话就要+1
}
//思路是让左括号入栈,如果遇到右括号就和栈顶元素进行比较,然后出栈
//首先创建一个栈
bool isValid(char * s){
ST ps;
STackInit(&ps);
while(*s){//遇到\0停止
if(*s=='('||*s=='{'||*s=='['){//左括号入栈
STackPush(&ps,*s);
}
else{
if(STEmpty(&ps)){
return false;
}
char top=STTop(&ps);//遇到右括号,获取栈顶元素
STackPop(&ps);//弹出一个元素
if(top!='('&& *s==')'||
top!='{'&&*s=='}'||
top!='['&&*s==']'){
return false;
}
}s++;
}
bool flag= STEmpty(&ps);
STackDestroy(&ps);
return flag;
}
如有问题,欢迎大佬批评指正