数据结构——栈

基本概念

栈是一种逻辑结构,是特殊的线性表。特殊在:

  • 只能在固定的一端操作

只要满足上述条件,那么这种特殊的线性表就会呈现一种“后进先出”的逻辑,这种逻辑就被称为栈。栈在生活中到处可见,比如堆叠的盘子、电梯中的人们、嵌套函数的参数等等。

由于约定了只能在线性表固定的一端进行操作,于是给栈这种特殊的线性表的“插入”、“删除”,另起了下面这些特定的名称:

  • 栈顶:可以进行插入删除的一端
  • 栈底:栈顶的对端
  • 入栈:将节点插入栈顶之上,也称为压栈,函数名通常为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;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值