目录
入栈:void Push(Stack* SL,Type x)
计算栈元素个数:int StackSize(Stack* SL)
结构定义:
typedef int Type;
//动态增长的栈
struct Stack
{
Type* arr;
int capacity;
int pop;//栈顶指针
};
用动态数组的方式队栈结构进行定义,而不用单链表,一是因为数组在CPU访问缓存时的命中率高,二是在进行入栈是,避免了链表尾插时每次都需要找尾节点的情况.
初始化: void STInit(Stack* SL)
功能是为arr开辟4个元素类型的空间,capacity记录当前栈区容量,pop指向栈顶元素的下一个位置:
void STInit(Stack* SL)
{
assert(SL);
SL->arr = (Type*)malloc(sizeof(Type)*4);
SL->capacity = 4;
SL->pop = 0;
//关于pop指针,初始指针有两种初始方式
//1.指向栈顶元素的下一个元素
//2.指向栈顶元素
//不同的初始化要求在入栈时pop++ 与元素入栈的顺序
}
传参问题:
如果传入的形参类型是Stack SL,那么在堆区开辟空间后,初始化函数栈帧内SL的arr成员内的参数没有返回,所以需要传入一个栈指针,修改主函数内的arr成员参数:
栈顶指针初始值:
两种初始化方式:
1. 指向栈顶元素
2.指向栈顶元素的下一个位置
初始化值不同,影响后续函数的设计,用前者,在写入栈函数时,pop先++,元素再入栈.
使用后者,元素直接入栈,pop再++.
入栈:void Push(Stack* SL,Type x)
void Push(Stack* SL,Type x)
{
assert(SL);
if (SL->capacity == SL->pop)
{
Type* tmp = (Type*)realloc(SL->arr, sizeof(Type) * SL->capacity * 2);
if (tmp == NULL)
{
perror("capacity realloc error");
return;
}
SL->arr = tmp;
SL->capacity *= 2;
}
SL->arr[SL->pop] = x;
SL->pop++;
}
要先判断SL指针是否为NULL,参数检查会在程序出错时迅速定位到问题代码段.
进来的第一步是先要判断栈区空间是否已经满,如果满了就重新开辟空间,栈区空间是否满的判断条件:SL->pop==SL->capacity;
开辟好空间后或者栈区仍有足够的空间就将元素入栈,这里初始化函数pop的初始值为0,即设置pop指针指向栈顶元素的下一个位置,所以先将元素入栈,然后再++;
出栈:void pop(Stack* SL)
void pop(Stack* SL)
{
assert(SL);
assert(SL->pop);
SL->pop--;
}
两种情况需要处理:
1.SL->pop要求SL指针不能为空,所以需要断言传入的栈指针是否有效.
2.当栈为空时,即pop==0,这时再--,会把pop赋值为-1,由于pop初始化值为0,要求元素先入栈再++,所以pop==-1会导致非法访问,所以需要断言处理SL->pop.
判断栈是否为空:bool Empty(Stack* SL)
bool Empty(Stack* SL)
{
assert(SL);
return SL->pop == 0;
}
这么简单的一个函数为什么还要封装呢?直接在主函数内用SL.pop==0判断不就好了吗??
答: 你使用栈并且初始化的时候,你并不知道pop是采用的哪种初始化方式,当栈为空时,pop==0还是==-1使用者并不清楚,所以还是让设计者来实现一个函数.
计算栈元素个数:int StackSize(Stack* SL)
int StackSize(Stack* SL)
{
assert(SL);
return SL->pop ;
}
为什么要封装这个函数理由同上!
返回栈顶元素:Type Top(Stack* SL)
Type Top(Stack* SL)
{
return SL->arr[SL->pop - 1];
}
其实在设计出栈函数时,可以考虑将元素出栈的同时返回,但是由于C++代码考虑到向前的兼容性,所以分开实现.
栈销毁:void Destory(Stack* SL)
void Destory(Stack* SL)
{
free(SL->arr);
SL->capacity = 0;
SL->pop = 0;
}
OJ:
括号匹配问题:
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/valid-parentheses
bool isValid(char * s)
{
Stack SL;
STInit(&SL);
while(*s!='\0')
{
if(*s=='('||*s=='['||*s=='{')
{
Push(&SL,*s);
}
else
{
if(!Empty(&SL))
{
Type e=Top(&SL);
pop(&SL);
if((*s==')'&&e!='(')||(*s==']'&&e!='[')||(*s=='}' && e!='{'))
{
Destory(&SL);
return false;
}
}
else
{
Destory(&SL);
return false;
}
}
s++;
}
bool ret=Empty(&SL);
Destory(&SL);
return ret;
}