栈是一种先入后出(或是后入先出)的结构,通常插入和删除操作的一端称为栈顶,另一端称为栈底。
栈的结构也是线性表的一种,结构实现一般可以用数组或者链表实现,相对而言数组的结构实现更优一点。因为数组在尾上插入数据的代价比较小。
结构选择以及思路:
如果用数组栈的话:可以通过malloc一个动态空间,创建一个指针指向这个空间,为了判断空间的容量情况,可以通过创建一个top记录有效数据数量,一个capacity记录空间最大容量。
如果用链式栈的话:如果用尾做栈顶,尾插尾删,如果不设计成双向链表,每次尾删都需要遍历找尾,很麻烦。如果用头做栈顶的话,头插头删,这样就可以设计较方便的单链表。
其实在系统中的内存有一部分被划分的内存区域也被称为 栈,函数的栈帧就是在这一块区域内开辟,所以一些临时变量也储存在这个区域。栈中,空间使用自上而下(由高地址到低地址),并且栈很小,一般8M。
而本章的数据结构中的栈,它是我们通过在系统内存中的堆区进行模拟实现出来的,它随着程序结束而销毁。堆存储着我们用动态内存开辟的变量,空间总体由下而上(但并不是一定上一个地址低于下一个,可能使用后又销毁),堆区很大,一般以G为单位。
正因为堆区很大,栈区很小,所以当一些使用递归的算法递归太深出现栈溢出,并且不好使用迭代进行实现的时候,可以通过在堆区模拟栈,从而实现算法。
头文件:Stack.h
//Stack.h
#include ;
#include ;
#include ; //断言所需要调的库
#include ;//布尔型调用的库
typedef int STDataType;
typedef struct stack {
STDataType* a;//动态空间指针
int top;
int capacity;
}ST;
//所需要实现的函数
//初始化栈
void StackInit(ST*ps);
//销毁栈
void StackDestroy(ST* ps);
//入栈
void StackPush(ST* ps, STDataType x);
//出栈
void StackPop(ST* ps);
//返回栈顶元素
STDataType StackTop(ST* ps);
//栈的有效数据个数
int StackSize(ST* ps);
//判断栈是否为空
bool StackEmpty(ST* ps);
测试页:Test.c
//Test.c
#include"Stack.h"
void StackTest1() {
ST st;//定义结构体变量
StackInit(&st);
StackPush(&st, 1);
//以下可以自己调用测试
...
}
int main(){
StackTest1();
return 0;
}
数组栈用动态顺序表这种更优的方式实现。
接下来的函数操作在源文件Stack.c中实现。
//Stack.c
//栈的初始化
#include"Stack.h"
void StackInit(ST* ps) {
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;//或者-1
}
先将结构体ST中的值先初始化,为了接下来的操作观察更方便。
//Stack.c
//栈的销毁
void StackDestroy(ST* ps) {
free(ps->a); //释放a指针指向的空间
ps->a = NULL;
ps->capacity = ps->top = 0;//一切回归初始
}
//Stack.c
//入栈操作
void StackPush(ST* ps, STDataType x) {
if (ps->capacity == ps->top) { //先判断 有效数据数是否等于最大容量
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//建立新的容量变量
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);//用realloc 扩建空间大小
if (tmp == NULL) {
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp; //赋值给结构体中的
ps->capacity = newcapacity;
}
ps->a[ps->top++] = x; //赋值入栈 top++
}
//Stack.c
//出栈操作
void StackPop(ST* ps) {
/*assert(ps->top > 0);*/
assert(!StackEmpty(ps));//如果为不为空程序继续,否则报错。
ps->top--;//先进先出 直接--
}
//Stack.c
//判空操作
bool StackEmpty(ST* ps) {
return ps->top==0; //说明栈里的有效数据数为0,栈为空
}
//Stack.c
//取栈顶数据操作
STDataType StackTop(ST* ps) {
assert(!StackEmpty(ps)); //为空报错
return ps->a[ps->top-1];//因为每次在给top位置赋值后,top会++
}
//Stack.c
//取栈有效数据数
int StackSize(ST* ps) {
return ps->top; //就是top的数
}
如果用头做栈顶,头插头删,这样就可以设计较方便的单链表。
头文件:SLStack.h
//STStack.h
#pragma once
#include
#include
#include
#include
typedef int SDataType;
typedef struct stack {
struct stack* next;
SDataType data;
}ST;
void StackPush(ST** top,SDataType x);
void StackPop(ST** top);
SDataType StackTop(ST** top);
int StackSize(ST** top);
bool StackEmpty(ST** top);
void StackDestroy(ST** top);
测试页:SLTest.c
#include"SLStack.h"
void Test1() {
ST* ps=NULL;
StackPush(&ps, 1);
printf("%d", StackTop(&ps));
StackPush(&ps, 2);
printf("%d", StackTop(&ps));
StackPush(&ps, 3);
printf("%d", StackTop(&ps));
StackPush(&ps, 4);
printf("%d\n", StackTop(&ps));
printf("%d\n", StackSize(&ps));
while (!StackEmpty(&ps)) {
printf("%d", StackTop(&ps));
StackPop(&ps);
}
StackDestroy(&ps);
}
int main() {
Test1();
return 0;
}
先创建单个结点
//SLStack.c
#include"SLStack.h"
//创建单个结点
ST* StackBuy(SDataType x) {
ST* newnode = (ST*)malloc(sizeof(ST));
if (newnode == NULL) {
printf("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//SLStack.c
//入栈
void StackPush(ST** top, SDataType x) {
assert(top);//如果传的是指针变量的值 报错
ST* newnode = StackBuy(x);
if (*top == NULL) { //为空直接指向新结点
*top = newnode;
}
else {
newnode->next = *top; //链表头插
*top = newnode;
}
}
//SLStack.c
//出栈
void StackPop(ST** top) {
assert(!StackEmpty(top));
if ((*top)->next == NULL) {
free(*top);
*top = NULL;
}
else {
ST* prev = (*top)->next; //链表头删
free(*top);
*top =prev;
}
}
//SLStack.c
//判空
bool StackEmpty(ST** top) {
return (*top)==NULL;
}
//SLStack.c
//销毁栈
void StackDestroy(ST** top) {
ST* cur = *top;
while (cur) {
ST* next = cur->next;
free(cur);
cur = next;
}
}
//SLStack.c
//查看栈顶值
SDataType StackTop(ST** top) {
assert(!StackEmpty(top));
return (*top)->data;
}
//SLStack.c
//查看有效数据量
int StackSize(ST** top) {
assert(!StackEmpty(top));
int i = 1;
ST* cur = *top;
while (cur->next) {
cur = cur->next;
i++;
}
return i;
}
C语言代码实现
//...
//由于C没有栈,在这之前先得把C中实现的栈搬过来。
//头文件和源文件
//...
bool isValid(char * s){
ST st;
StackInit(&st);
while(*s)
{
//左括号全部入栈
if(*s == '{' || *s=='[' || *s=='(')
{
StackPush(&st, *s);
}
//遇到右括号出栈匹对
else
{
//针对空与右括号多的情况
if(StackEmpty(&st))
{
return false;
}
STDataType top = StackTop(&st);
if((*s == '}' && top != '{')
|| (*s == ']' && top != '[')
|| (*s == ')' && top != '('))
{
return false;
}
StackPop(&st);
}
s++;
}
//针对左括号多的情况
bool flag = StackEmpty(&st);
return flag;
}
栈的实现总体来说,用数组实现是非常容易的,链式栈的实现相对有点复杂,链式栈如果用尾插尾删的方式实现入栈进栈,需要每次遍历尾部结点,所以通过头插头删实现会更加便利。
以下是完整代码:
完整代码