-->


栈的概念

  • 栈是数据结构重要的思想,在数据结构中,有两种方式来存储数据
    • 链式结构和顺序结构,

下面会有链式结构和顺序结构分别存储的例子

以顺序结构举例:
栈遵循后进先出的规则LIFO(Last Input First Output ),栈只能在一端进行操作

  • 插入元素:入栈(Push)、压栈。
  • 取出元素:出栈(Pop)、弹栈。

由于栈只能在一端操作,所以在操作的那一端叫做栈顶(Top,另外一端叫做栈底(Bottom

没有数据的栈叫做空栈

image

顺序存储栈(顺序栈)

  • 在使用栈的时候需要注意在哪一端操作,如果是顺序表来实现栈,那么在尾部操作是最好的。

  • 栈尾就是顺序表头部,栈顶顺序表的尾部。

  • 实现顺序栈和顺序表一样需要有管理者

image

//这个存储的数据类型是可以修改的,遍历输出的时候需要修改输出的转换符
//顺序栈存放的数据类型
typedef int DataType_t;

//1.需要建立顺序栈的管理结构体
typedef struct seqstack
{
    DataType_t * Bottom;      //顺序栈的首地址
    unsigned int Size;      //顺序栈的容量
    int          Top;      //顺序栈的存储个数
}SeqStack_t;

创建顺序栈

/****************************************************
*       函数名称:   SeqStack_Create
*       函数功能:   新建顺序栈
*       函数参数:   
*                   @size:创建顺序栈的大小
*       返回结果:   返回顺序栈管理者的地址
*       注意事项:   None
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
SeqStack_t * SeqStack_Create(unsigned int size)
{
    //1.首先需要一个管理者
    SeqStack_t * Manager = (SeqStack_t *)calloc(1,sizeof(SeqStack_t));
    if(NULL == Manager){
        perror("calloc for Manager is failed!!!");
        exit(-1);
    }
    //2.建立顺序栈的堆空间,堆空间的首地址由管理者的addr成员管理
    Manager->Bottom = (DataType_t*) calloc(size,sizeof(DataType_t));
    if(NULL == Manager->Bottom){
        perror("calloc for Manager is failed!!!");
        free(Manager);
        Manager = NULL;
        exit(-1);
    }

    //3.顺序栈创建完成需要初始化
    Manager->Size = size;
    //元素的下标从-1开始表示没有元素
    Manager->Top = -1;

    return Manager;
}

入栈

  • 在入栈之前需要判断栈是否已满

image

/****************************************************
*       函数名称:   SeqStack_IsFull
*       函数功能:   判断顺序栈是否已满
*       函数参数:   
*                   @Manager:顺序栈管理者的地址
*       返回结果:   true:已满。false:未满。
*       注意事项:   None
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool SeqStack_IsFull(SeqStack_t * Manager)
{
    return (Manager->Top+1 == Manager->Size) ? true : false ;
}

/****************************************************
*       函数名称:   SeqStack_TailPushEle
*       函数功能:   给顺序栈尾部添加新的元素
*       函数参数:   
*                   @Manager:顺序栈管理者的地址
*                   @data   :添加的数据
*       返回结果:   true:添加成功。false:添加失败。
*       注意事项:   None
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool SeqStack_TailPushEle(SeqStack_t * Manager,DataType_t data)
{
    //1.首先判断顺序栈是否已满
    if( SeqStack_IsFull(Manager) ){
        printf("sequence stack is full!\n");
        return false;
    }

    //2.未满可以插入,在尾部插入,插入完成之后也需要个数需要加一
    Manager->Bottom[++Manager->Top] = data;

    return true;
}

出栈

  • 在入栈之前需要判断栈是否为空

image

/****************************************************
*       函数名称:   SeqStack_IsEmpty
*       函数功能:   判断顺序栈是否为空
*       函数参数:   
*                   @Manager:顺序栈管理者的地址
*       返回结果:   true:为空。false:不为空。
*       注意事项:   None
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool SeqStack_IsEmpty(SeqStack_t * Manager)
{
    return (Manager->Top == -1) ? true : false ;
}

/****************************************************
*       函数名称:   SeqStack_TailPopEle
*       函数功能:   在顺序栈的尾部取元素
*       函数参数:   
*                   @Manager:顺序栈管理者的地址
*       返回结果:   返回删除的数据(需要取出),没有返回值代表没取出来
*       注意事项:   None
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
DataType_t SeqStack_TailPopEle(SeqStack_t * Manager)
{
    //1.首先需要顺序栈是否为空
    if(SeqStack_IsEmpty(Manager)){
        printf("sequence stack is empty!\n");
        return ;
    }
	
	//存储需要返回的数据
	DataType_t temp = Manager->Bottom[Manager->Top];
	
    Manager->Top--;

    return temp;
}

遍历输出栈

/***************************************************
*       函数名称:   SeqStack_TraEle
*       函数功能:   遍历输出每一个元素
*       函数参数:   
*                   @Manager    :顺序栈管理者的地址
*       返回结果:   true:遍历成功。false:遍历失败。
*       注意事项:   None
*       函数作者:
*       创建日期:   2024/04/20
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool SeqStack_TraEle(SeqStack_t * Manager)
{
    //1.首先需要顺序栈是否为空
    if(SeqStack_IsEmpty(Manager)){
        printf("sequence stack is empty!\n");
        return false;
    }  

    //2.直接访问元素
    for(int i = 0;i <= Manager->Top;i++){
        printf("%d element is %d\n",i+1,Manager->Bottom[i]);
    }

    return true;
}

删除栈

/***************************************************
*       函数名称:   SeqStack_Dle
*       函数功能:   删除顺序栈
*       函数参数:   
*                   @Manager    :顺序栈管理者的地址
*       返回结果:   true:删除成功。false:删除失败。
*       注意事项:   None
*       函数作者:
*       创建日期:   2024/04/20
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool SeqStack_Dle(SeqStack_t * Manager)
{
    //1.判断管理者是否存在
    if(NULL == Manager){
        printf("sequence stack is not exist");
        return false;
    }
    //2.先把顺序栈删除,在删除管理者
    free(Manager->Bottom);
    Manager->Bottom = NULL;
    free(Manager);
    Manager = NULL;
    return true;
}

需要的头文件

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

链式存储栈(链栈)

  • 使用单向不循环链表来实现栈

  • 在使用栈的时候需要注意在哪一端操作,如果是链表来实现栈,那么在头部操作是最好的。

  • 栈尾就是链表尾部,栈顶链表的头部。

  • 栈的节点
    image

//链栈节点下面是数据的数据类型
typedef char DataType_t;

//带头结点的链栈
//1.每一个节点都有自己的元素和连接下一个元素的地址
typedef struct linkstack
{
    DataType_t          data;   //链栈节点存放的数据
    struct linkstack     *next;  //链栈节点存放下一个链栈节点的地址
}LkStack_t;

image

创建的栈

/***************************************************
*       函数名称:   LkStack_CreatList
*       函数功能:   创建一个新的链栈
*       函数参数:   None
*       返回结果:   返回链栈的地址:创建成功。NULL:创建失败。
*       注意事项:   None
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
LkStack_t * LkStack_CreatList(void)
{
    //在堆空间申请一个空间存放链栈的头结点
    LkStack_t * Head = (LkStack_t *)calloc(1,sizeof(LkStack_t));
    if(NULL == Head){
        perror("calloc memory for head is failed!!!");
        return NULL;
    }

    //头结点的初始化,数据不需要初始化
    Head->next = NULL;

    //返回头结点的地址
    return Head;
}

/***************************************************
*       函数名称:   LkStack_NewNode
*       函数功能:   创建一个链栈新的节点
*       函数参数:   
*                   @data   :创建链栈节点时,下面存放的数值
*       返回结果:   返回链栈的地址:创建成功。NULL:创建失败。
*       注意事项:   一般都在插入的时候需要使用,不会单独使用
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
LkStack_t * LkStack_NewNode(DataType_t data)
{
    //1.先从堆空间申请内存存放链栈节点
    LkStack_t * New = (LkStack_t *)calloc(1,sizeof(LkStack_t));
    if(NULL == New){
        perror("Calloc memory for NewNode is failed!");
        return NULL;
    }

    //初始化链栈节点
    New->data = data;
    New->next = NULL;
    
    //申请成功直接返回
    return New;
}

入栈

image

/***************************************************
*       函数名称:   LkStack_HeadPushNode
*       函数功能:   在头节点的后面插入新的节点
*       函数参数:   
*                   @Head   :头结点的地址
*                   @data   :创建链栈节点时,下面存放的数值
*       返回结果:   true:插入成功;false:插入失败
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool LkStack_HeadPushNode(LkStack_t * Head,DataType_t data)
{
    //1.先判断链栈是否存在
    if(NULL == Head){
        printf("Linkstack head is not exist!!!\n");
        return false;
    }

    //创建新的节点
    LkStack_t * New = LkStack_NewNode(data);
    if(NULL == New){
        return false;
    }

    //如果存在,直接把头结点指向的地址赋值给新的节点下面的地址
    New->next = Head->next;
    Head->next = New;

    return true;
}

出栈

image

  • 在出栈之前需要判断栈是否为空
/***************************************************
*       函数名称:   LkStack_IsEmpty
*       函数功能:   判断链栈是否为空
*       函数参数:   
*                   @Head   :头结点的地址
*       返回结果:   true:为空;false:不为空
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool LkStack_IsEmpty(LkStack_t * Head)
{
	return (Head->next == NULL) ? true : false;
}

/***************************************************
*       函数名称:   LkStack_HeadPopNode
*       函数功能:   把链栈的首结点取出
*       函数参数:   
*                   @Head   :头结点的地址
*       返回结果:   返回取出的数据;没有值取出失败
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
DataType_t LkStack_HeadPopNode(LkStack_t * Head)
{
    //1.先判断链栈是否存在
    if(NULL == Head){
        printf("Linkstack head is not exist!!!\n");
        return;
    }
	
	//判断链栈是否为空
	if(LkStack_IsEmpty(Head)){
		printf("Linkstack is empty!!!\n");
		return;
	}
	
    //定义变量存储当前结点直接让他指向首节点的地址,以便于释放删除的结点
    LkStack_t *Phead = Head->next;
	DataType_t temp = Phead->data;
    //寻找到数据节点可以开始删除
    Head->next = Phead->next;
	Phead->next = NULL;
    free(Phead);
    Phead = NULL;

    return temp;
}

遍历输出栈

/***************************************************
*       函数名称:   LkStack_TraNode
*       函数功能:   遍历输出每一个结点的数据
*       函数参数:   
*                   @Head   :头结点的地址
*       返回结果:   true:遍历成功;false:遍历失败
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool LkStack_TraNode(LkStack_t * Head)
{
    //1.先判断链栈是否存在
    if(NULL == Head){
        printf("Linkstack head is not exist!!!\n");
        return false;
    }
	
	//判断链栈是否为空
	if(LkStack_IsEmpty(Head)){
		printf("Linkstack is empty!!!\n");
		return false;
	}
	
    //备份数据,直接给首结点数据
    LkStack_t * Phead = Head->next;
    int i = 1;//输出第几个数据
    //遍历到最后的节点,输出数据
    while(Phead->next){
        printf("[%d]th Node is %c\n",i,Phead->data);
        i++;
        Phead = Phead->next;
    }
    printf("[%d]th Node is %c\n",i,Phead->data);

    return true;
}

删除栈

/***************************************************
*       函数名称:   LkStack_Del
*       函数功能:   删除链栈
*       函数参数:   
*                   @Head   :头结点的地址
*       返回结果:   true:删除成功;false:删除失败
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/05/02
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool LkStack_Del(LkStack_t * Head)
{
    //1.先判断链栈是否存在
    if(NULL == Head){
        printf("Linkstack head is not exist!!!\n");
        return false;
    }

    //让Head先向前走,然后后面跟着Head的前驱节点,删除前驱节点
    LkStack_t * Head_prev = Head;
    Head = Head->next;
    //遍历到最后的节点,输出数据
    while(Head){
        //Head不为空直接删除直接前驱,
        Head_prev->next = NULL;
        free(Head_prev);

        Head_prev = Head;
        Head = Head->next;
    }
    //Head为空,退出去删除直接前驱和Head本身.
    Head_prev->next = NULL;
    free(Head_prev);
	Head_prev = NULL;
 
    return true;
}

需要的头文件

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
posted @ 2024-05-06 20:00  wuju  阅读(26)  评论(0)    收藏  举报