目录
栈的作用
栈的实现
栈的数据结构
栈的初始化
栈的销毁
栈的插入
栈的删除
获得栈顶元素
获得栈有效元素个数
判断栈是否为空
栈的使用
完整代码
栈是一种特殊结构的线性表
先来看看栈的图
之所以说它特殊,是因为它的插入删除功能比较特殊
栈的插入也叫作压栈/入栈/进栈
栈的插入只能在栈顶插入
栈的删除也叫作出栈
栈的删除只能在栈顶删除
栈的作用
栈这种特殊的结构可以让我们在解决特定问题的时候会有很大的帮助
类似的例如队列也是
这也是我们学习多种数据结构的原因
栈的实现
我们要实现的栈是能够动态扩容的栈,而不是静态栈
栈可以使用两种数据结构实现,一种是顺序表,一种是链表,这里是通过顺序表为底层实现的
因为静态在现实中并不常用,缺点也很明显(容易浪费空间或者空间不足)
下面先来看看栈的数据结构
栈的数据结构
typedef int STDataType;
typedef struct Stack
{
STDataType* _a;
int _top; // 栈顶
int _capacity; // 容量
}Stack;
结构体里有三个成员
1、_a是指向一块连续空间的指针
2、_top是栈顶(主要用于入栈出栈)
3、_capacity是栈的容量(便于后续调整_a指针指向的空间的大小)
栈的初始化
void StackInit(Stack* ps)
{
assert(ps);
ps->_a = NULL;
ps->_capacity = 0;
ps->_top = 0;
}
这里初始化暂时先让_a指针为NULL
所以capacity容量当然为0
也可以选择先malloc一块空间,先让_a指针指向它,后续再扩容,但capacity也要跟着一起改
这里top的初始化就比较有争议了
top刚开始可以为0,也可以刚开始为-1
若top刚开始为0,则top表示栈顶数据的最后一个元素的下一个元素的下标,也可以表示栈的大小(size)
若top刚开始为-1,则top表示最后有效数据的下标(例如栈中有3个数据,那么top为3,top则为最后一个数据的下标)
这里我们选择了top=0,则下面写的代码也是为top=0而准备的
栈的销毁
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->_a);
ps->_a = NULL;
ps->_capacity = 0;
ps->_top = 0;
}
销毁只需要释放掉_a指针指向的空间,并设为NULL,capacity、top=0即可
栈的插入
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
if (ps->_capacity == ps->_top)
{
int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail\n");
return;
}
ps->_a = tmp;
ps->_capacity = newcapacity;
}
ps->_a[ps->_top] = data;
ps->_top++;
}
因为我们要插入数据,那么插入数据之前我们要先判断_a指针所指向的空间还是否有剩余,判断条件就是capacity = top,若条件满足则表示空间不足,则需要扩容
第一个newcapacity用了一个三目操作符,若刚开始的空间为0,则初始化newcapacity为4,否则newcapacity = capacity * 2
然后使用了一个tmp指针接收我们realloc函数的返回值,并为_a空间扩容
这里若_a = NULL,则realloc会当作malloc先开辟一块空间,并让tmp指向这块空间,所以到后面我们需要让_a = tmp
若tmp = NULL,则代表realloc扩容失败,perror函数打印错误信息,并return
不若则扩容成功,当_a获取到新的空间后我们直接在栈顶的位置插入data即可
最后让top++往后走
栈的删除
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->_top > 0);
ps->_top--;
}
删除只需要让top--即可(注意断言防止top越界)
为什么我们要单独写一个函数调用来让top--而不是在主函数中直接让top--?
因为若是别人使用我们实现的栈,别人并不知道我们的top的含义是什么
也就是我们刚开始讲的top初始化为0和top初始化为-1的区别
获得栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->_top > 0);
return ps->_a[ps->_top - 1];
}
直接返回top-1位置的值即可
获得栈有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->_top;
}
前面也写了有效元素的个数即为top
所以直接返回top即可
判断栈是否为空
int StackEmpty(Stack* ps)
{
assert(ps);
return ps->_top == 0;
}
若top为0则代表empty为空
栈的使用
int main()
{
Stack st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
while (!StackEmpty(&st))
{
int tmp = StackTop(&st);
StackPop(&st);
printf("%d ", tmp);
}
StackDestroy(&st);
}
首先我们向栈中入了四个元素,1,2,3,4
若我们要将它们全部打印出来只能向上面一样写个循环
先判断若栈不为空,则循环继续
先保存栈顶元素到tmp中
将栈顶元素删除(这样才能取到下一个元素)
然后打印我们刚刚取到的栈顶元素(也就是刚刚删除掉的元素)
最后别忘了将栈销毁,防止内存泄漏
完整代码
Stack.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
STDataType* _a;
int _top; // 栈顶
int _capacity; // 容量
}Stack;
// 初始化栈
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
Stack.c
#include "Stack.h"
void StackInit(Stack* ps)
{
assert(ps);
ps->_a = NULL;
ps->_capacity = 0;
ps->_top = 0;
}
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
if (ps->_capacity == ps->_top)
{
int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail\n");
return;
}
ps->_a = tmp;
ps->_capacity = newcapacity;
}
ps->_a[ps->_top] = data;
ps->_top++;
}
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->_top > 0);
ps->_top--;
}
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->_top > 0);
return ps->_a[ps->_top - 1];
}
int StackSize(Stack* ps)
{
assert(ps);
return ps->_top;
}
int StackEmpty(Stack* ps)
{
assert(ps);
return ps->_top == 0;
}
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->_a);
ps->_a = NULL;
ps->_capacity = 0;
ps->_top = 0;
}
Test.c
#include "Stack.h"
void TestStack()
{
Stack st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
while (!StackEmpty(&st))
{
int tmp = StackTop(&st);
StackPop(&st);
printf("%d ", tmp);
}
StackDestroy(&st);
}
int main()
{
TestStack();
return 0;
}
完