关于“栈”,有一个非常贴切的例子,就是一摞叠在一起的盘子。我们平时放盘子的时候,都是从下往上一个一个放;取的时候,我们也是从上往下一个一个地依次取,不能从中间任意抽出。后进者先出,先进者后出,这就是典型的“栈”结构。
从栈的操作特性上来看,栈是一种“操作受限”的线性表,只允许在一端插入和删除数据。
第一次接触这种数据结构的时候,就对它存在的意义产生了很大的疑惑。因为我觉得,相比数组和链表,栈带给我的只有限制,并没有任何优势。那我直接使用数组或者链表不就好了吗?为什么还要用这个“操作受限”的“栈”呢?
事实上,从功能上来说,数组或链表确实可以替代栈,但你要知道,特定的数据结构是对特定场景的抽象,而且,数组或链表暴露了太多的操作接口,操作上的确灵活自由,但使用时就比较不可控,自然也就更容易出错。
当某个数据集合只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性,这时我们就应该首选“栈”这种数据结构。
栈主要包含两个操作,入栈和出栈,也就是在栈顶插入一个数据和从栈顶删除一个数据。理解了栈的定义之后,我们来看一看如何用代码实现一个栈。
栈的具体实现
栈是一种 “特殊” 的线性存储结构,因此栈的具体实现有以下两种方式:
实际上,栈既可以用数组来实现,也可以用链表来实现。用数组实现的栈,我们叫作顺序栈,用链表实现的栈,我们叫作链式栈。
两种实现方式的区别,仅限于数据元素在实际物理空间上存放的相对位置,顺序栈底层采用的是数组,链栈底层采用的是链表。
支持动态扩容
顺序栈:栈的顺序存储是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。通常的习惯做法是以top =0,表示空栈。鉴于C语言中数组的下标约定从0开始,则当以C作描述语言时,如此设定会带来很大不便;另一方面,由于栈在使用过程中所需最大空间的大小很难估计,因此,一般来说,在初始化设空栈时不应限定栈的最大容量。一个较合理的做法是:先为栈分配一个基本容量,然后在应用过程中,当栈的空间不够使用时再逐段扩大。为此,可以设置两个常量;STACK_INIT_SIZE(存储空间初试分配量),STACKINCREMENT(存储空间分配增量)。
//顺序栈
//stack.h 文件
#ifndef __STACK_H__
#define __STACK_H__
#include
typedef struct Stack
{
int *base;//保存栈的数据
int top;//栈顶指针,当前可以存放数据的下标
int stacksize;//总格子数
}Stack,*PStack;
#define STACK_INIT_SIZE (5)
#define STACKINCREMENT (5)
void InitStack(PStack ps);//初始化
bool Push(PStack ps,int val);//入栈
bool IsEmpty(PStack ps);//判空
bool GetTop(PStack ps,int *rtval);//获取栈顶元素的值,但不删除
//rtval:输出参数,子函数把值传回父函数
bool Pop(PStack ps,int *rtval);//得到栈顶值并删除
void Destroy(PStack ps);//摧毁
bool StackPrint(PStack ps);//打印栈元素
#endif // !__STACK_H__
//stack.c 文件
#include
#include
#include
#include
#include"stack.h"
/**
* @brief 初始化
*
* @param ps
*/
void InitStack(PStack ps)
{
assert(ps!=NULL);
ps->base = (int *)malloc(STACK_INIT_SIZE*sizeof(int));
ps->top = 0;
ps->stacksize = STACK_INIT_SIZE;
}
/**
* @brief 判满
*
* @param ps 栈指针
* @return true
* @return false
*/
static bool IsFull(PStack ps)
{
return ps->top == ps->stacksize;
}
/**
* @brief 扩容
*
* @param ps 栈指针
*/
static void Inc(PStack ps)
{
ps->base =(int *)realloc(ps->base,(ps->stacksize + STACKINCREMENT) * sizeof(int));
ps->stacksize += STACKINCREMENT;
}
/**
* @brief 入栈: 插入元素val为新的栈顶元素
*
* @param ps 栈指针
* @param val 插入元素
* @return true
* @return false
*/
bool Push(PStack ps,int val)
{
if (IsFull(ps))
{
Inc(ps);
}
ps->base[ps->top] = val;
ps->top++;
return true;
}
/**
* @brief 判空
*
* @param ps
* @return true
* @return false
*/
bool IsEmpty(PStack ps)
{
return ps->top == 0;
}
/**
* @brief 获取栈顶元素的值,但不删除
* 若栈不为空,则用rtval 返回栈ps的栈顶元素
* @param ps
* @param rtval
* @return true
* @return false
*/
bool GetTop(PStack ps,int *rtval)
{
if (IsEmpty(ps))
{
return false;
}
if (rtval!=NULL)
{
*rtval = ps->base[ps->top-1];
}
return true;
}
/**
* @brief 出栈:得到栈顶值并删除
* 若栈不空,则删除栈ps栈顶元素,用rtval返回其值
* @param ps
* @param rtval
* @return true
* @return false
*/
bool Pop(PStack ps,int *rtval)
{
if (IsEmpty(ps))
{
return false;
}
if (rtval!=NULL)
{
*rtval = ps->base[ps->top-1];
}
ps->top--;
return true;
}
/**
* @brief 摧毁栈ps,不再存在
*
* @param ps
*/
void Destroy(PStack ps)
{
free(ps->base);
ps->base = NULL;
ps->top = 0;
ps->stacksize = 0;
}
/**
* @brief 打印栈
*
* @param ps
*/
bool StackPrint(PStack ps)
{
int i = 0;
if (IsEmpty(ps))
{
return false;
}
for (; i < ps->top; i++)
{
printf("%d ", ps->base[i]);
}
printf("\n");
return true;
}
//test.c 测试文件
void test(void)
{
PStack s;
int topValue = 0;
InitStack(s);
Push(s,1);
Push(s,2);
Push(s,3);
Push(s,4);
Push(s,5);
Push(s,6);
StackPrint(s);
Pop(s,&topValue);
printf("pop : %d\r\n",topValue);
StackPrint(s);
GetTop(s,&topValue);
printf("GetTop : %d\r\n",topValue);
}
/**
* @brief 测试函数
*
* @return int
*/
int main(void)
{
test();
system("pause");
return 0;
}