目录
栈的基本概念:
栈是一种基于先进后出(FILO)或者后进先出(LIFO)的数据结构,是一种只能在一端经行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进的数据被压入栈底,后进女的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。
压栈(入栈):数据进入到栈的动作。
弹栈(出栈):数据从栈中出去的动作。
栈的种类:顺序栈和链式栈
顺序栈:使用数组实现;
链式栈:使用链表实现;
一、顺序栈:
顺序栈可以使用一维数组实现,base指针指向栈底(数组的第0个元素),top指针是动态的,每次都指向栈顶元素(最后一个放入栈中的元素),因此,我们将base指针称之为:栈底指针,将top指针称之为栈顶指针。
1、顺序栈的描述结构体
/*******************顺序栈************************/
#define size_init_len 10
#define expand_num 20 //用于对栈的扩增
//顺序栈描述结构体
typedef struct SqStack{
ElemType *top;//栈顶指针
ElemType *base;//栈底指针,指向栈空间的首地址
uint stacklen;//栈能够存储的最大元素个数
uint len;//实际存储的元素的个数
}SqStack;
2、顺序栈的初始化
/*
* @brief 初始化一个顺序链表
* @param 初始顺序栈长度
* @return 返回初始化后的栈的指针
* */
SqStack * Stastack_Init(uint size)
{
//创建一个栈
SqStack *s=(SqStack *)malloc(sizeof(SqStack));
//栈分配存储空间
s->base =(ElemType *)malloc(size * sizeof(ElemType)); //指向栈空间的首地址
s->top = s->base;//初始化的时候站内无元素,栈顶指向栈尾
s->stacklen = size;//栈的空间大小
s->len=0;/当前位置
return s;
}
3、对顺序栈经行扩容
/*
* @brief 对栈进行扩容
* @param s 需要扩容的栈的指针
* @return 成功返回TRUE,失败返回FALSE
* */
int expand(SqStack *s)
{
printf("[%s %d] SqStack expand....\n",__FUNCTION__ ,__LINE__);
//先判断这个栈是否为空
if (NULL == s)
{
printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
return FALSE;
}
//为栈的存储空间重新分配空间
s->base=(ElemType*)realloc(s->base,(s->stacklen + expand_num)*sizeof(ElemType));
s->top = s->base + s->stacklen;
s->stacklen += expand_num;
return TRUE;
}
4、入栈
/* * @brief 入栈
* * @param s 栈指针
* * @param data 需要入栈的元素
* * @return 成功返回TRUE, 失败返回FALSE * */
int push(SqStack *s,ElemType data)
{
if (NULL == s)
{
printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
return FALSE;
}
//如果栈满了
if (s->top - s->base >= s->stacklen)
expand(s);//进行扩栈
*(s->top) = data;
s->top++;
return TRUE;
}
5、出栈
/* * @brief 出栈
* * @param s 栈指针
* * @param data 返回出栈元素
* * @return 成功返回TRUE, 失败返回FALSE * */
int pop(SqStack *s,ElemType * data)
{
if (NULL == s|| NULL==data)
{
printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
return FALSE;
}
//先让栈顶指针--,指向栈顶的元素
s->top--;
*data = *(s->top);将出栈后的栈顶元素赋值给data
}
6、获取栈顶元素
/* * @brief 获取栈顶元素
* * @param s 栈指针
* * @param data 存放栈顶元素的指针
* * @return 成功返回TRUE, 失败返回FALSE * */
int get_top_elem(SqStack *s,ElemType *data)
{
if(NULL==s||NULL==data)
return FALSE;
//如果栈为空
*data=*(s->top-1);//栈顶指向的是元素的尾指针,要想获取元素的值,应指向元素的头指针
return TRUE;
}
7、清空顺序栈
/* * @brief 清空顺序表,直接将top=base即可
* * @param s 栈指针
* * @return 成功返回TRUE, 失败返回FALSE * */
int empty_stastack(SqStack *s)
{
if(NULL==s||s->top==s->base){//如果栈为空
printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
return FALSE;
}
s->top=s->base;//对于顺序栈来说清空栈的化直接将栈顶指向栈尾即可,下一次入栈会覆盖掉之前的值
return TRUE;
}
8、链表的销毁
/* * @brief 销毁顺序表
* * @param s 栈指针
* * @param data 存放栈顶元素的指针
* * @return 成功返回TRUE, 失败返回FALSE * */
int stack_destroy(SqStack *s)
{
if(s==NULL)
{
printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
return FALSE;
}
if(s->base!=0)
{
free(s->base);
s->top=s->base=NULL;
s->stacklen=0;
}
return TRUE;
}
9、打印栈内元素
//打印栈内元素
void printf_Sqs(SqStack *s)
{
if (NULL == s)
{
printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
}
ElemType *num=s->top;
while(num!=s->base)
{
num--;
printf("%d ",*num);
}
printf("%d ",*num);
//判断栈是否为空
if(s->top==s->base)
{
printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
}
}
10、主函数
int main() {
SqStack *s=Stastack_Init(size_init_len);
//进栈测试--begin
int i;
for(i=0;i<20;i++)
{
push(s,100+i*2);
}
printf_Sqs(s);
//进栈测试--end
//出栈测试--begin
ElemType data;
pop(s,&data);
printf("\nout:%d\n",data);
printf_Sqs(s);
get_top_elem(s,&data);
printf("\ntap:%d\n",data);
//使用循环的方式出栈
while(s->top!=s->base)
{
pop(s,&data);
printf("\n%d\n",data);
}
printf_Sqs(s);
//出栈测试--end
//获取栈顶元素
return 0;
}
二、链式栈
链式栈就是使用链表的方式实现栈,为了实现先进后出(FILO),我们可以采用链表的“头插法”,实现一个链式栈,值得注意的是,与我们之前学习的链表不同的是,链式栈不需要头结点。
#include <stdio.h>
#include <stdlib.h>
#define FALSE 0
#define TRUE 1
typedef unsigned int uint;
typedef int ElemType ;
/*
* 链式栈就是使用链表的方式实现栈,为了实现先进后出,使用链表中的头插法实现一个链式栈
* 注意:链式栈中不需要头结点
* */
//链式栈的描述:
typedef struct StackNode{
ElemType data;
struct StackNode *next;
}StackNode,*LinkStack;
//链式栈的初始化
/*
* @brief 初始化一个链表栈
* @return 返回链式栈的栈顶指针
* */
LinkStack stack_init()
{
LinkStack s;
s=NULL;
return s;
}
/*
* @brief //入栈:链表的头插法在链表的第一个结点之前插入新的结点
* @param s 栈顶指针地址
* @param data 需要入栈的元素
* @return 成功返回TRUE,失败返回FALSE
* */
int Push(LinkStack *s,ElemType data)
{
if(s==NULL)
{
printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
return FALSE;
}
StackNode *p=(StackNode *)malloc(sizeof(StackNode));
p->data=data;
//新的结点的next指向链表的第一个结点
p->next = *s;
//栈顶指针指向新的结点
*s = p;
return TRUE;
}
/*
* @brief 将栈内打印出来
* @param s 栈顶指针地址
* @return 成功返回TRUE,失败返回FALSE
* */
int printf_LinkStack(LinkStack s)
{
if(s==NULL)
{
printf("[%s %d] stack pointer is NULL ...\n", __FUNCTION__ , __LINE__);
return FALSE;
}
StackNode *p=s;
while (p!=NULL)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
return TRUE;
}
/*
* @brief 出栈
* @param s 栈顶指针地址
* @param data 返回值
* @return 成功返回TRUE,失败返回FALSE
* */
int pup(LinkStack *s,ElemType *data)
{
if(s==NULL)
{
printf("[%s %d] stack pointer is NULL ...", __FUNCTION__ , __LINE__);
return FALSE;
}
*data=(*s)->data;
StackNode *t=(*s)->next;
free(*s);
*s=t;
return TRUE;
}
/*
* @brief 获取栈顶元素
* @param s 栈顶指针地址
* @param data 返回值
* @return 成功返回TRUE,失败返回FALSE
* */
int Get_top(LinkStack *s,ElemType *top_val)
{
if(s==NULL)
{
printf("[%s %d] stack pointer is NULL ...", __FUNCTION__ , __LINE__);
return FALSE;
}
*top_val=(*s)->data;
return TRUE;
}
/*
* @brief 销毁链式表
* @param s 栈顶指针地址
* @return 成功返回TRUE,失败返回FALSE
* */
int destroy_LinkStack(LinkStack *s)
{
if(s==NULL)
{
printf("[%s %d] stack pointer is NULL ...", __FUNCTION__ , __LINE__);
return FALSE;
}
StackNode *t = *s;;
while(t)
{
t = (*s)->next;
free(*s);
*s = t;
}
return TRUE;
}
int main()
{
LinkStack s=stack_init();//s是一个指针类型存放 栈顶指针 代表一个链式栈
//入栈
int i;
for(i=0;i<10;i++)
{
Push(&s,100+2*i);
}
printf_LinkStack(s);
//出栈
int data;
pup(&s,&data);
printf("Out:%d\r\n",data);
printf_LinkStack(s);
//获取栈顶元素
int top;
Get_top(&s,&top);
printf("top_val:%d\r\n",top);
printf_LinkStack(s);
//销毁链式栈
destroy_LinkStack(&s);
printf_LinkStack(s);
return 0;
}