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;
}


运行结果:

image

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;
}

运行结果:image

posted @ 2026-07-26 14:29  !城北徐公.!  阅读(6)  评论(0)    收藏  举报