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

数据结构——顺序栈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;
}

http://www.kler.cn/a/594964.html

相关文章:

  • 力扣刷题——143.重排链表
  • 多数据源@DS和@Transactional踩坑之路
  • 【负载均衡系列】Nginx
  • 到底爱不爱我
  • stm32-定时器
  • GITLAB部署安装教程
  • JNI介绍
  • 算法及数据结构系列 - 二分查找
  • 游戏引擎学习第172天
  • 深度解析历年蓝桥杯算法题,助力提升编程技能
  • Saga 模式实战 Demo
  • Compose 实践与探索十五 —— 自定义触摸
  • Prometheus Exporter系列-Postgres_Exporter一键部署
  • Java 大视界 -- Java 大数据分布式计算中的通信优化与网络拓扑设计(145)
  • Python 单例模式的 5 种实现方式:深入解析与最佳实践
  • 如何给商品一键换色?图生生AI,告别繁琐修图
  • 【Dify平台】Function Call 模式模式和ReAct模型有什么不同?
  • Compose 实践与探索十六 —— 与传统的 View 系统混用
  • Q2 电商订单数据分析优化
  • QT Quick(C++)跨平台应用程序项目实战教程 3 — 项目基本设置(窗体尺寸、中文标题、窗体图标、可执行程序图标)