1. 基本概念
栈是一种逻辑结构,是特殊的线性表。特殊在:
- 只能在固定的一端操作
只要满足上述条件,那么这种特殊的线性表就会呈现一种“后进先出”的逻辑,这种逻辑就被称为栈。栈在生活中到处可见,比如堆叠的盘子、电梯中的人们、嵌套函数的参数等等。
由于约定了只能在线性表固定的一端进行操作,于是给栈这种特殊的线性表的“插入”、“删除”,另起了下面这些特定的名称:
- 栈顶:可以进行插入删除的一端
- 栈底:栈顶的对端
- 入栈:将节点插入栈顶之上,也称为压栈,函数名通常为push()
- 出栈:将节点从栈顶剔除,也称为弹栈,函数名通常为pop()
- 取栈顶:取得栈顶元素,但不出栈,函数名通常为top()
基于这种固定一端操作的简单约定,栈获得了“后进先出”的基本特性,如下图所示,最后一个放入的元素,最先被拿出来:
2. 存储形式
栈只是一种数据逻辑,如何将数据存储于内存则是另一回事。一般而言,可以采用顺序存储形成顺序栈,或采用链式存储形成链式栈。
- 顺序栈
顺序存储意味着开辟一块连续的内存来存储数据节点,一般而言,管理栈数据除了需要一块连续的内存之外,还需要记录栈的总容量、当前栈的元素个数、当前栈顶元素位置,如果有多线程还需要配互斥锁和信号量等信息,为了便于管理,通常将这些信息统一于在一个管理结构体之中:
struct seqStack
{
datatype *data; // 顺序栈入口
int size; // 顺序栈总容量
int top; // 顺序栈栈顶元素下标
};
- 链式栈
链式栈的组织形式与链表无异,只不过插入删除被约束在固定的一端。为了便于操作,通常也会创建所谓管理结构体,用来存储栈顶指针、栈元素个数等信息:
// 链式栈节点
typedef struct node
{
datatype data;
struct node *next;
}node;
// 链式栈管理结构体
struct linkStack
{
node *top; // 链式栈栈顶指针
int size; // 链式栈当前元素个数
};
3. 基本操作
不管是顺序栈,链式栈,栈的操作逻辑都是一样的,但由于存储形式不同,代码的实现是不同的。下面分别将顺序栈和链式栈的基本核心操作罗列出来:
- 顺序栈
// 顺序栈管理结构体
typedef struct
{
datatype *data; // 顺序栈入口
int size; // 顺序栈总容量
int top; // 顺序栈栈顶元素下标
}seqStack;
// 初始化空栈
seqStack *initStack(int size)
{
seqStack *s = (seqStack *)malloc(sizeof(seqStack))
if(s != NULL)
{
s->data = (datatype *)malloc(sizeof(datatype) * size));
if(s->data == NULL)
{
free(s);
return NULL;
}
s->size = size;
s->top = -1;
}
return s;
}
// 判断栈是否已满
bool isFull(seqStack *s)
{
return s->top == s->size-1;
}
// 判断栈是否为空
bool isEmpty(seqStack *s)
{
return s->top == -1;
}
// 入栈
bool push(seqStack *s, datatype data)
{
if(isFull(s))
return false;
s->data[++s->top] = data;
return true;
}
// 出栈
bool pop(seqStack *s, datatype *pm)
{
if(top(s, pm) == false)
return false;
s->top--;
return true;
}
// 取栈顶元素
bool top(seqStack *s, datatype *pm)
{
if(isEmpty(s))
return false;
*pm = s->data[s->top];
return true;
}
- 链式栈
// 链式栈节点
typedef struct node
{
datatype data;
struct node *next;
}node;
// 链式栈管理结构体
typedef struct linkStack
{
node *top; // 链式栈栈顶指针
int size; // 链式栈当前元素个数
}linkStack;
// 初始化空栈
linkStack * initStack(void)
{
linkStack * s = (linkStack *)malloc(sizeof(linkStack));
if(s != NULL)
{
s->top = NULL;
s->size = 0;
}
return s;
}
// 判断栈是否为空
bool isEmpty(linkStack *s)
{
return (top->size == 0);
}
// 入栈
bool push(linkStack *s, datatype data)
{
// 创建链表节点
node *new = (node *)malloc(sizeof(node));
if(new == NULL)
return false;
new->data = data;
// 将节点置入栈顶
new->next = s->top;
s->top = new;
// 更新栈元素个数
s->size++;
return true;
}
// 出栈
bool pop(linkStack *s, datatype *pm)
{
if(isEmpty(s))
return false;
linkStack *tmp = s->top;
// 将原栈顶元素剔除出栈
s->top = tmp->next;
tmp->next = NULL;
// 返回栈顶元素,并释放节点
*pm = tmp->data;
free(tmp);
return true;
}
// 取栈顶元素
bool top(linkStack *s, datatype *pm)
{
if(isEmpty(s))
return false;
// 返回栈顶元素
*pm = s->top->data;
return true;
}