2026/7/26 数据结构6(栈)
一、栈的原理与应用
由前文,对于顺序表而言改查比较容易,增删比较麻烦,对于链式表而言,增删比较简单,改查比较麻烦,所以每种数据结构都有不同的特点,用户需要选择合适的数据结构。
栈(stack),存储货物或供旅客住宿的地方,可引申为仓库、中转站,所以引入到计算机领域里,就是指数据暂时存储的地方,所以才有进栈(PUSH)、出栈(POP)的说法。栈是一种特殊的线性表,特殊在栈的一端是封闭的,数据的插入与删除只能在栈的另一端进行,也就是栈遵循“后进先出”的原则。也被成为“LIFO”结构,意思是“last input first output”。
之前学习linux内存分区的时候,已经知道栈内存自顶向下进行递增,其实栈和顺序表以及链式表都一样,都属于线性结构,存储的数据的逻辑关系也是一对一的。
闭合的一端被称为栈底(Stack Bottom),允许数据的插入与删除的一端被称为栈顶(Stack Top),不包含任何元素的栈被称为空栈。
由于栈也是一种线性结构,所以可以以数组或者链表作为基础,在此基础上实现栈的操作。
1.以数组作为基础实现栈空间(顺序栈)
数组在内存中占用一块连续的空间,也就是数组元素的内存地址是连续的。为了实现栈,一般是把数组头作为栈底,数组头部到数组尾部作为栈的增长方向,也就是用户只在数组尾部对数据进行插入和删除。
编写如下C语言代码实现顺序栈的构造,入栈,出栈并验证。
/********************************************************************************************************
* @File Name :SequenceStack.c
* @Author :bbzhu04@gmail.com
* @Date :2026/07/26
* @Function :构造函数实现顺序栈元素的增删改查等操作
* @Notes :为了提高可移植性,所以顺序栈中元素的数据类型为DataType_t,用户可以根据实际情况修改顺序栈中元素的类型
* 另外,为了方便管理顺序栈,所以用户设计SeqStack_t结构体,该结构体中包含三个成员:栈底地址+栈容量+栈顶元素的下标
* Copyright (c) 2025-2026 bbzhu04@gmail.com All right Reserved
* ******************************************************************************************************/
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
//指的是顺序栈中的元素的数据类型,用户可以根据需要进行修改
typedef int DataType_t;
//构造记录顺序栈SequenceStack各项参数(栈底地址+栈容量+栈顶元素的下标)的结构体
typedef struct SequenceStack
{
DataType_t * Bottom; //记录栈底地址
unsigned int Size; //记录栈容量
int Top; //记录栈顶元素的下标
}SeqStack_t;
/********************************************************************************************************
* @Function Name :SeqStack_Create
* @Functionality :创建顺序栈并对顺序栈进行初始化
* @Function Parameters :None
* @Return result :指向顺序栈管理结构体的指针
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
SeqStack_t * SeqStack_Create(unsigned int size)
{
//1.利用calloc为顺序栈的管理结构体申请一块堆内存
SeqStack_t *Manager = (SeqStack_t *)calloc(1,sizeof(SeqStack_t));
if(NULL == Manager)
{
perror("calloc memory for manager is failed");
exit(-1); //程序异常终止
}
//2.利用calloc为所有元素申请堆内存
Manager->Bottom = (DataType_t *)calloc(size,sizeof(DataType_t));
if (NULL == Manager->Bottom)
{
perror("calloc memory for Stack is failed");
free(Manager);
exit(-1); //程序异常终止
}
//3.对管理顺序栈的结构体进行初始化(元素容量 + 栈顶下标)
Manager->Size = size; //对顺序栈中的容量进行初始化
Manager->Top = -1; //由于顺序栈为空,则栈顶元素的下标初值为-1
return Manager;
}
/********************************************************************************************************
* @Function Name :SeqStack_IsFull
* @Functionality :判断顺序栈是否已满
* @Function Parameters :
* @Manager:指向顺序栈管理结构体的指针
* @Return result :bool型变量,顺序栈已满时返回true,否则返回false
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
bool SeqStack_IsFull(SeqStack_t *Manager)
{
return (Manager->Top + 1 == Manager->Size) ? true : false;
}
/********************************************************************************************************
* @Function Name :SeqStack_IsEmpty
* @Functionality :判断顺序栈是否为空
* @Function Parameters :
* @Manager:指向顺序栈管理结构体的指针
* @Return result :bool型变量,顺序栈为空时返回true,否则返回false
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
bool SeqStack_IsEmpty(SeqStack_t *Manager)
{
return (-1 == Manager->Top) ? true : false;
}
/********************************************************************************************************
* @Function Name :SeqStack_Push
* @Functionality :入栈操作
* @Function Parameters :
* @Manager:指向顺序栈管理结构体的指针
* @Data :要入栈的元素值
* @Return result :bool型变量,入栈成功时返回true,失败返回false
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
bool SeqStack_Push(SeqStack_t *Manager, DataType_t Data)
{
//1.判断顺序栈是否已满
if ( SeqStack_IsFull(Manager) )
{
printf("SeqStack Full is Full!\n");
return false;
}
//2.如果顺序栈有空闲空间,则把新元素添加到顺序栈的栈顶
Manager->Bottom[++Manager->Top] = Data;
return true;
}
/********************************************************************************************************
* @Function Name :SeqStack_Pop
* @Functionality :出栈操作
* @Function Parameters :
* @Manager:指向顺序栈管理结构体的指针
* @Return result :出栈元素的值
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
DataType_t SeqStack_Pop(SeqStack_t *Manager)
{
DataType_t temp = 0; //用于存储出栈元素的值
//1.判断顺序栈是否为空
if ( SeqStack_IsEmpty(Manager) )
{
printf("SeqStack is Empty!\n");
return 0;
}
//2.由于删除了一个元素,则需要让顺序栈的栈顶元素下标-1
temp = Manager->Bottom[Manager->Top--];
return temp;
}
/********************************************************************************************************
* @Function Name :SeqStack_Print
* @Functionality :遍历顺序栈的元素
* @Function Parameters :
* @Manager :指向顺序栈管理结构体的指针
* @Return result :None
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
void SeqStack_Print(SeqStack_t *Manager)
{
for (int i = 0; i <= Manager->Top; ++i) //遍历顺序栈中的元素
{
printf(" Stack Element[%d] = %d\n",i,Manager->Bottom[i]);
}
}
int main(int argc, char const *argv[])
{
//创建顺序栈
SeqStack_t *Manager = SeqStack_Create(5);
//1.入栈操作
SeqStack_Push(Manager, 10);
SeqStack_Push(Manager, 20);
SeqStack_Push(Manager, 30);
//遍历顺序栈的元素
SeqStack_Print(Manager);
//2.出栈操作
DataType_t poppedElement = SeqStack_Pop(Manager);
printf("Popped Element: %d\n", poppedElement);
//遍历顺序栈的元素
SeqStack_Print(Manager);
return 0;
}
运行结果:

2.以链表作为基础实现栈空间(链式栈)
如果打算实现链式栈,一般是以链表作为基础,一般是把链表头部作为栈顶,方便数据的插入和删除(头插+头删),链式栈相当于是一个单向不循环的链表。
编写如下C语言代码实现链式栈的构造,入栈,出栈并验证。
/********************************************************************************************************
* @File Name :LinkedStack.c
* @Author :bbzhu04@gmail.com
* @Date :2026/07/26
* @Function :构造函数实现链栈的构造,入栈,出栈等操作
* @Notes :为了提高可移植性,所以链表中元素的数据类型为DataType_t,用户可以根据实际情况修改链表中元素的类型
* 另外,为了方便管理链表,所以用户设计LList_t结构体,该结构体中包含两个成员:结点的数据域和指针域
* Copyright (c) 2025-2026 bbzhu04@gmail.com All right Reserved
* ******************************************************************************************************/
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
//指的是单向链表中的结点有效数据类型,用户可以根据需要进行修改
typedef int DataType_t;
//构造记录链栈各项参数(栈顶指针)的结构体
typedef struct LinkedStack
{
DataType_t data; //记录结点的数据域
struct LinkedStack *next; //记录栈顶指针
}LStack_t;
/********************************************************************************************************
* @Function Name :LStack_Create
* @Functionality :创建链栈并对链栈进行初始化
* @Function Parameters :None
* @Return result :指向链栈管理结构体的指针
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
LStack_t * LStack_Create(void)
{
//1.创建一个头结点并对头结点申请内存
LStack_t *Top = (LStack_t *)calloc(1,sizeof(LStack_t));
if (NULL == Top)
{
perror("Calloc memory for Head is Failed");
exit(-1);
}
//2.对头结点进行初始化,头结点的指针域置为空
Top->next = NULL;
//3.把头结点的地址返回即可
return Top;
}
/********************************************************************************************************
* @Function Name :LStack_NewNode
* @Functionality :创建新的结点,并对新结点进行初始化(数据域 + 指针域)
* @Function Parameters :
* @data:要插入到新结点中的数据
* @Return result :指向链栈新结点的指针
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
LStack_t * LStack_NewNode(DataType_t data)
{
//1.创建一个新结点并对新结点申请内存
LStack_t *New = (LStack_t *)calloc(1,sizeof(LStack_t));
if (NULL == New)
{
perror("Calloc memory for NewNode is Failed");
return NULL;
}
//2.对新结点的数据域和指针域进行初始化
New->data = data;
New->next = NULL;
return New;
}
/********************************************************************************************************
* @Function Name :LStack_Push
* @Functionality :入栈操作
* @Function Parameters :
* @Top:指向链栈的指针
* @data:要插入到新结点中的数据
* @Return result :bool型变量,插入成功时返回true,否则返回false
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
bool LStack_Push(LStack_t *Top,DataType_t data)
{
//1.创建新的结点,并对新结点进行初始化
LStack_t *New = LStack_NewNode(data);
if (NULL == New)
{
printf("can not insert new node\n");
return false;
}
//2.判断链表是否为空,如果为空,则直接插入即可
if (NULL == Top->next)
{
Top->next = New;
return true;
}
//3.如果链表为非空,则把新结点插入到链表的头部
New->next = Top->next;
Top->next = New;
return true;
}
/********************************************************************************************************
* @Function Name :LStack_Pop
* @Functionality :出栈操作
* @Function Parameters :
* @Top:指向链栈的指针
* @Return result :被删除结点的数据
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
DataType_t LStack_Pop(LStack_t *Top)
{
//1.判断链表是否为空,如果为空,则直接返回即可
if (NULL == Top->next)
{
printf("Stack is empty\n");
return -1; //返回一个错误值,表示栈为空
}
//2.删除链表的首结点
LStack_t *Ptop = Top->next;
DataType_t data = Ptop->data;
Top->next = Ptop->next; //头结点跳过旧栈顶
free(Ptop); //释放原本栈顶结点
return data;
}
/********************************************************************************************************
* @Function Name :LStack_Print
* @Functionality :遍历输出链栈的元素
* @Function Parameters :
* @Top:指向链栈的指针
* @Return result :void
* @Notes :None
* @Author :bbzhu04@gmail.com
* @Revision History :None
* @Function version :V1.0
* *****************************************************************************************************/
void LStack_Print(LStack_t *Top)
{
//对链栈的栈顶指针进行备份
LStack_t *Ptop = Top->next;
//判断链栈是否为空,如果为空,则直接插入即可
if (NULL == Top->next)
{
printf("Stack is empty\n");
}
while(Ptop)//指针域不为空,则说明当前结点不是尾结点
{
//输出栈顶结点的数据域
printf("data = %d\n",Ptop->data);
//把栈顶结点的后继作为新的栈顶结点
Ptop = Ptop->next;
}
}
int main(int argc, char const *argv[])
{
//1.创建链栈
LStack_t *Stack = LStack_Create();
//2.入栈操作
LStack_Push(Stack,1);
LStack_Push(Stack,2);
LStack_Push(Stack,3);
LStack_Print(Stack); //遍历输出链栈的元素
printf("\n"); //3 2 1
//3.出栈操作
DataType_t data1 = LStack_Pop(Stack);
printf("Pop data = %d\n",data1);
LStack_Print(Stack); //遍历输出链栈的元素 //2 1
printf("\n");
DataType_t data2 = LStack_Pop(Stack);
printf("Pop data = %d\n",data2);
LStack_Print(Stack); //遍历输出链栈的元素 //1
printf("\n");
DataType_t data3 = LStack_Pop(Stack);
printf("Pop data = %d\n",data3);
LStack_Print(Stack); //遍历输出链栈的元素 //空栈
printf("\n");
return 0;
}
运行结果:
作者:bbzhu04@gmail.com ,多有不妥之处,望批评指正
本文原创,欢迎转载;转载请在文章显著位置标明原文链接: href="https://www.cnblogs.com/zhubb/p/21931004"
保留所有权限,未经许可禁止篡改、商用发布。

浙公网安备 33010602011771号