当前位置: 首页 > news >正文

数据结构——顺序栈seq_stack

前言:大家好😍,本文主要介绍了数据结构——顺序栈

目录

一、概念

1.1 顺序栈的基本概念

1.2 顺序栈的存储结构

二、基本操作

2.1 结构体定义

2.2 初始化 

2.3 判空

2.4 判满

2.5 扩容

2.6 插入 入栈

2.7 删除 出栈

2.8 获取栈顶元素

2.9 获取有效值长度

2.10 清空

2.11 销毁

2.12 打印

2.13 测试


一、概念

顺序栈是栈的一种实现方式,它是基于顺序存储结构(通常是数组或动态分配的内存空间)实现的栈。栈是一种**后进先出(LIFO,Last In First Out)**的数据结构,这意味着最后放入栈中的元素会最先被取出。

1.1 顺序栈的基本概念

  1. 栈的定义

    • 栈是一种线性表,其操作主要在表的一端进行,这一端称为栈顶(Top),另一端称为栈底(Bottom)

    • 栈的操作遵循**后进先出(LIFO)**的原则,即最后放入栈中的元素最先被取出。

  2. 顺序栈的特点

    • 顺序栈使用连续的内存空间来存储栈中的元素,通常通过数组或动态分配的内存来实现。

    • 栈底的位置是固定的,通常用一个指针(如base)来标记栈底。

    • 栈顶的位置是动态变化的,用一个指针(如top)来表示栈顶的位置。

1.2 顺序栈的存储结构

顺序栈的存储结构通常包括以下部分:

ps是一个指向Seq_Stack结构体的指针,所以    ps可以指向结构体的成员

  • base:指向栈底的指针,标记栈的起始位置。

  • top:栈顶指针,表示栈顶的位置。top的值通常等于栈中元素的数量。

  • stacksize:表示栈的总容量,即栈可以存储的最大元素数量。

二、基本操作

2.1 结构体定义

typedef char ELEM_TYPE;
#define INITSIZE 10//*2

struct Seq_Stack
{
	ELEM_TYPE* base;//指针,用来接收malloc的返回值
	int top;//栈顶指针
	int stacksize;//当前总的格子数
};
typedef struct Seq_Stack Seq_Stack;
typedef struct Seq_Stack* PSeq_Stack;

base是一个指针,它的作用是标记栈的底部位置,也就是这块连续内存空间的起始地址。有了base,我们就能找到栈的“根基”,从而操作整个栈。无论栈顶指针top如何变化(入栈或出栈),base始终指向栈的底部,确保我们不会迷失方向。

2.2 初始化 

void Init_Seq_Stack(Seq_Stack* ps)
{
    assert(ps != NULL);

    ps->base = (ELEM_TYPE*)malloc(INITSIZE * sizeof(ELEM_TYPE));
    if (ps->base == NULL)//内存分配失败
    {
        exit(1);//程序调用exit终止运行
    }

    ps->top = 0;
    ps->stacksize = INITSIZE;
}
  • 参数Seq_Stack* ps 是一个指向Seq_Stack结构体的指针。这个结构体定义了栈的基本属性,比如栈底指针、栈顶指针和栈的容量等。

  • ps->base:这是栈底指针,用来存储malloc分配的内存地址分配完内存后,ps->base就指向了这块内存的起始位置,也就是栈的底部。malloc:这是一个动态内存分配函数,用来在堆上分配一块内存空间

  • ps->top = 0:栈顶指针top初始化为0。在顺序栈中,top的值通常表示栈中元素的数量。初始时栈为空,所以top为0。

  • ps->stacksize = INITSIZE:将栈的总容量stacksize设置为初始大小INITSIZE。这样我们就知道栈一开始可以存储多少个元素。

  • 通过ps,函数可以访问和修改这个栈的内部数据(如basetopstacksize)。

2.3 判空

//2.判空(判断顺序表有没有元素)
bool Is_Empty(Seq_Stack* ps)
{
    if (ps == NULL)
        exit(1);

    return ps->top ==0;
}
  • 这个指针ps就是用来告诉函数“我要检查的是哪一个栈”。

  • if (ps == NULL)来检查指针是否为空。如果是空的,说明传入的栈是无效的,程序会调用exit(1)终止运行,避免后续操作出错。

2.4 判满

bool Is_Full(Seq_Stack* ps)
{
    if (ps == NULL)
        return false;

    return ps->top == ps->stacksize;
}

2.5 扩容

//4.扩容
static void Inc(Seq_Stack*ps)
{
    assert(ps != NULL);
    if (ps == NULL)
        return;

    ELEM_TYPE* tmp = (ELEM_TYPE*)realloc(ps->base, ps->stacksize * sizeof(ELEM_TYPE) * 2);
    if (tmp != NULL)
        ps->base = tmp;

    ps->stacksize *= 2;
}

Inc函数用于将顺序栈的容量加倍。从而允许更多的元素入栈。

  • realloc:这是一个动态内存分配函数,用来重新分配一块内存空间。它接受两个参数:

    • 第一个参数是当前内存块的指针(这里是ps->base)。

    • 第二个参数是新的内存大小(这里是ps->stacksize * sizeof(ELEM_TYPE) * 2)。ps->stacksize是当前栈的容量,sizeof(ELEM_TYPE)是每个元素的大小,* 2表示将容量加倍。

    • tmp:这是一个临时指针,用来存储realloc返回的新内存地址。如果realloc成功,它会返回新的内存块的指针;如果失败,它会返回NULL

    • 如果tmp不为NULL,说明realloc成功,新的内存已经分配好了。此时,ps所指向的结构体中的base成员更新为tmp的值。,即让栈底指针指向新的内存地址。

    • 将栈的总容量stacksize加倍。

2.6 插入 入栈

//插入元素(入栈/压栈)   Push
bool Push(Seq_Stack* ps, ELEM_TYPE val)
{
    assert(ps != NULL);
    if (ps == NULL)
        return false;

    if (Is_Full(ps))
    {
        Inc(ps);
    }

    ps->base[ps->top] = val;
    ps->top++;

    return true;
}
  • ps->base[ps->top] = val;:将新元素val插入到栈顶位置。ps->top表示栈顶指针,ps->base[ps->top]表示栈顶位置的内存地址。这里将新元素val存储到栈顶位置。

  • ps->top++;:将栈顶指针ps->top加1,表示栈中元素的数量增加了一个。

2.7 删除 出栈

bool Pop(Seq_Stack* ps)
{
    assert(ps != NULL);
    if (ps == NULL)
        return false;

    if (Is_Empty(ps))
        return false;

    ps->top--;

    return true;
}

2.8 获取栈顶元素

ELEM_TYPE Top(Seq_Stack* ps)
{
    assert(ps != NULL);
    if (ps == NULL)
        exit(1);

    if (Is_Empty(ps))
        exit(1);

    return ps->base[ps->top - 1];

    return true;
}
  • ps->base[ps->top - 1]:栈顶元素的索引是ps->top - 1,因为ps->top表示栈中元素的数量,而数组索引是从0开始的。因此,ps->base[ps->top - 1]表示栈顶元素的值。

2.9 获取有效值长度

//5.获取有效值长度  Size
int Get_length(Seq_Stack* ps)
{
    assert(ps != NULL);
    if (ps == NULL)
        return -1;

    return ps->top;
}

2.10 清空

//6.清空  不需要free
void Clear(Seq_Stack* ps)
{
    assert(ps != NULL);
    if (ps == NULL)
        return;

    ps->top = 0;
}

2.11 销毁

//7.销毁 需要free
void Destroy(Seq_Stack* ps)
{
    assert(ps != NULL);
    if (ps == NULL)
        return;

    free(ps->base);
    ps->base = NULL;
    ps->top = ps->stacksize = 0;
}

Destroy函数的作用是销毁一个顺序栈,并释放它所占用的动态内存。 

  • 调用free(ps->base)会释放ps->base指向的内存空间。释放内存后,这块内存就不再属于程序了,程序不能再使用它。

2.12 打印

//8.打印
void Show(Seq_Stack* ps)
{
    //assert
    for (int i = 0; i < ps->top; i++)
    {
        printf("%c ", ps->base[i]);
    }
    printf("\n");
}
  • ps->base:这是栈底指针,指向栈的起始内存地址。

  • ps->base[i]:表示栈中第i个元素的值。数组名本质上是一个指向数组首元素的指针。eg:arr&arr[0]是等价的,都表示数组的起始地址。

2.13 测试

//顺序栈测试用例
int main()
{
    /*srand((unsigned int)time(NULL));
    int tmp = rand() % 29 + 1;
    printf("%d\n", tmp);*/

    Seq_Stack head;
    Init_Seq_Stack(&head);

    Push(&head, 'A');
    Push(&head, 'B');
    Push(&head, 'C');

    Show(&head);//A B C
    printf("%c\n", Top(&head));//C
    Pop(&head);//A B
    Show(&head);//A B 

    return 0;
}

相关文章:

  • MySQL事务日志详解:Redo Log 和 Undo Log 的核心机制
  • 【大模型理论篇】CogVLM:多模态预训练语言模型
  • 详细说明脚本评估和耗时较长的任务
  • npm报错‘proxy‘ config is set properxy. See: ‘npm help config‘
  • 【负载均衡系列】HAProxy
  • 蓝桥杯 第十天 :2022 国赛 第 2 题 排列距离/康托定理
  • 20242817李臻《Linux⾼级编程实践》第四周
  • 使用 Python 实现时间序列预测:电力负载需求预测
  • SpringCloud负载均衡:Ribbon核心组件与策略配置
  • 【C++】:异常
  • 如何优化 React 应用性能?
  • Hugo 生成静态网站并部署到 GitHub Pages 的完整流程
  • 深入解析MediaPipe:强大的实时计算机视觉框架
  • 自由学习记录(45)
  • OBOO鸥柏丨广告机终端控制端KylinOS麒麟、统信UOS/鸿蒙国产系统
  • CTF类题目复现总结-[WUSTCTF2020]find_me 1
  • Flutter 快速接入Fair
  • PostgreSQL 数据库中导入大量数据
  • 如何为在线游戏选择合适的游戏盾?
  • .NET8使用EF Core连接SQLite
  • 夜读|尊重生命的棱角
  • “一码难求”的Manus开放注册但价格不菲,智能体距离“实用”还有多远
  • 在笔墨金石间,看胡问遂与梅舒适的艺术对话
  • 演员黄晓明、金世佳进入上海戏剧学院2025年博士研究生复试名单
  • 从普通人经历中发现历史,王笛解读《线索与痕迹》
  • 美股全线收涨:道指涨逾千点,纳斯达克中国金龙指数涨5.4%