栈
基本概念
栈是一种逻辑结构,是特殊的线性表。特殊在:
- 只能在固定的一端操作
只要满足上述条件,那么这种特殊的线性表就会呈现一种“后进先出”的逻辑,这种逻辑就被称为栈。栈在生活中到处可见,比如堆叠的盘子、电梯中的人们、嵌套函数的参数等等。
由于约定了只能在线性表固定的一端进行操作,于是给栈这种特殊的线性表的“插入”、“删除”,另起了下面这些特定的名称:
- 栈顶:可以进行插入删除的一端
- 栈底:栈顶的对端
- 入栈:将节点插入栈顶之上,也称为压栈,函数名通常为push()
- 出栈:将节点从栈顶剔除,也称为弹栈,函数名通常为pop()
- 取栈顶:取得栈顶元素,但不出栈,函数名通常为top()
基于这种固定一端操作的简单约定,栈获得了“后进先出”的基本特性,如下图所示,最后一个放入的元素,最先被拿出来:
存储形式
栈只是一种数据逻辑,如何将数据存储于内存则是另一回事。一般而言,可以采用顺序存储形成顺序栈,或采用链式存储形成链式栈。
- 顺序栈
顺序存储意味着开辟一块连续的内存来存储数据节点,一般而言,管理栈数据除了需要一块连续的内存之外,还需要记录栈的总容量、当前栈的元素个数、当前栈顶元素位置,如果有多线程还需要配互斥锁和信号量等信息,为了便于管理,通常将这些信息统一于在一个管理结构体之中:
// 顺序栈节点
struct seqStack
{
datatype *data; // 顺序栈入口
int size; // 顺序栈总容量
int top; // 顺序栈栈顶元素下标
};
- 链式栈
链式栈的组织形式与链表无异,只不过插入删除被约束在固定的一端。为了便于操作,通常也会创建所谓管理结构体,用来存储栈顶指针、栈元素个数等信息:
// 链式栈节点
typedef struct node
{
datatype data;
struct node *next;
}node;
// 链式栈管理结构体
struct linkStack
{
node *top; // 链式栈栈顶指针
int size; // 链式栈当前元素个数
};
基本操作
不管是顺序栈,链式栈,栈的操作逻辑都是一样的,但由于存储形式不同,代码的实现是不同的。下面分别将顺序栈和链式栈的基本核心操作罗列出来:
顺序栈
- sstack.h
#ifndef __SSTACK_H
#define __SSTACK_H
// 数据类型
typedef int DATA;
// 顺序栈结构体
typedef struct
{
DATA *pData; // 栈中元素的地址
int size; // 栈的总容量
int top; // 栈顶元素下标
}SeqStack;
// 初始化栈
int SStack_init(SeqStack *s, int num);
// 判断栈是否已满
int SStack_isfull(SeqStack *st);
// 判断栈是否为空
int SStack_isempty(SeqStack *st);
// 入栈/压栈
int SStack_push(SeqStack *st,DATA data);
// 出栈/弹栈
int SStack_pop(SeqStack *st,DATA *data);
// 回收栈
int SStack_free(SeqStack *st);
#endif
- sstack.c
#include "sstack.h"
// 初始化栈
int SStack_init(SeqStack *s,int size)
{
// 给栈中的元素申请存储空间
s->pData = (DATA*)calloc(sizeof(DATA),size);
// 校验
if(s->pData == NULL)
{
perror("内存申请失败!");
return -1;
}
// 初始化
s->size = size;
s->top = -1;
return 0;
}
// 判断栈是否已满
int SStack_isfull(SeqStack *s)
{
return s->top + 1 == s->size;
}
// 判断栈是否为空
int SStack_isempty(SeqStack *s)
{
return s->top == -1;
}
// 入栈|压栈
int SStack_push(SeqStack *s,DATA data){
// 判断栈是否已满
if(SStack_isfull(s))
{
printf("顺序栈已填满!\n");
return -1;
}
// 操作栈
s->top++;// top向后偏移一位,top自增
s->pData[s->top] = data;// 在更新后的top位置插入数据
return 0;
}
// 出栈|弹栈(弹出指定元素)
int SStack_pop(SeqStack *s,DATA *data)
{
// 判断栈是否为空
if(SStack_isempty(s))
{
printf("顺序栈已为空!\n");
return -1;
}
// 将弹出的元素返回
*data = s->pData[s->top];
// 改变top
s->top--;
// printf("弹出数据:%d\n",*data);
return 0;
}
// 回收栈
int SStack_destroy(SeqStack *s){
if(s->pData)
{
// 释放内存
free(s->pData);
s->pData = NULL;
}
// 重置top
s->top = -1;
}
链式栈
-
linkstack.h
#ifndef __LINKSTACK_H #define __LINKSTACK_H // 数据类型 typedef int DATA; // 链式栈节点 typedef struct _node { DATA data; // 数据 struct _node *next; // 指向下一个栈的节点 }NODE; // 链式栈管理结构体 typedef struct { NODE *pHead;// 链式栈栈顶指针 int size; // 链式栈当前元素个数 int num; }LinkStack; // 初始化链式栈 int LStack_init(LinkStack *s, int num); // 判断栈是否已满 int LStack_isfull(LinkStack *st); // 判断栈是否为空 int LStack_isempty(LinkStack *st); // 压栈/入栈 int LStack_push(LinkStack *st,DATA data); // 弹栈/出栈 int LStack_pop(LinkStack *st,DATA *data); // 回收栈 int LStack_free(LinkStack *st); #endif
-
linkstack.c
#include "linkstack.h" #include <stdio.h> #include <stdlib.h> // 初始化栈 int LStack_init(LinkStack *st, int num) { st->pHead = NULL; st->size = num; st->num = 0; return 0; } // 判断栈是否已满 int LStack_isfull(LinkStack *st) { return st->num == st->size; } // 判断栈是否为空 int LStack_isempty(LinkStack *st) { return st->num == 0; } // 入栈 int LStack_push(LinkStack *st, DATA data) { if (LStack_isfull(st)) return -1; NODE *p = (NODE *)malloc(sizeof(NODE)); if (!p) return -1; p->data = data; p->next = st->pHead; st->pHead = p; (st->num)++; return 0; } // 出栈 int LStack_pop(LinkStack *st, DATA *data) { if (LStack_isempty(st)) return -1; NODE *p = st->pHead; if (!p) return -1; *data = p->data; st->pHead = p->next; free(p); (st->num)--; return 0; } // 回收栈 int LStack_free(LinkStack *st) { NODE *p = st->pHead, *q = NULL; while (p) { q = p; p = p->next; free(q); } st->pHead = NULL; st->num = 0; return 0; }