数据结构——栈与队列(C语言版)

一:栈

        栈的概念:

       栈(Stack)是一种常见的数据结构,它基于后进先出( Last In, First Out, LIFO)的原则工作。这意味着最后进入栈的元素最先被移除,而最先进入栈的元素最后被移除。栈通常有两个主要操作:

        压栈(Push):将元素添加到栈的顶部。

        出栈(Pop):从栈的顶部移除元素。

   

         栈的特性决定了:栈只能访问栈顶的数据,并遵循着先入后出的原则,如果像访问栈底数据,只能将栈上剩余的数据全部依次弹出,不能去遍历,否则会破坏栈的性质。

        栈的实现:

                栈的结构:

                        栈可以用数组的方式实现也可以用链表的方式实现,但数组在存取元素时具有 O(1) 的时间复杂度。栈的基本操作包括压栈(push)和弹栈(pop),这两个操作在数组中可以通过索引直接进行,效率非常高。相比之下,链表的操作(除非是双向链表且操作在头部进行)通常需要 O(N) 的时间复杂度,数组在存储元素时,除了实际存储的数据外,还需要一定的额外空间来维护数组的结构。但是相比链表,链表需要额外的指针空间来维护节点间的连接关系,这会增加存储开销,使用数组实现栈比使用链表更为简单和直观。数组不需要处理节点间的指针关系,操作起来更加直接。 综上所述,栈的实现用数组会更加方便快捷。

        

这里用typedef 将int类型重新起了一个STDataType的名字,因为在存入栈数据的时候,不一定存的就是整型数据,也可能是字符型,在后续的维护修改中,只需要将typedef int STDataType改成

typedef char STDataType,可以大大减小维护的成本。 接着定义一个名为Stack的结构体,重命名为ST。

        STDataType*a:表示一个指向栈的内存的指针

        int top:表示指向栈顶有效数据的下一个位置

        int capaticy:表示栈数据的容量

                栈的初始化与销毁:
                       初始化:

                        

        第一步:断言asset(ps),表示如果传进来的参数为NULL就报错。

        第二步:使用malloc申请一块空间给ps->a(栈指针),大小为16个字节(4个STDataType),如果申请失败,那么就进行报错并结束程序。

        第三步:初始化将capaticy容量初始化为4,并将top初始化为0;

                出栈:

        

第一步:先判断传入的数据是否为NULL;

第二步:判断栈结构体里是否还有数据,如果没有数据是不能进行删除的。

第三步:将top--,这里不需要去管top里的值,因为后续插入的会进行覆盖。

                入栈:

第一步:进行断言assert(ps);

第二步:检查有效数据是否等于容量,如果等于则进行扩容;

第三步:将需要传入的数据放入到top位置,并将top++。top指向的是有效数据的下一个位置,所以直接放在top位置上就行。

                计算栈的有效数据长度:

                访问栈顶元素:

这里只需要注意top为指向的是有效数据的下一个位置,在取栈顶数据的时候将top-1,取得有效数据。

                判断栈里是否有数据:

如果top等于零,栈中没有数据那么就条件为真返回true,反之返回false。

二:队列

        队列的概念:

        队列(Queue)是一种常见的数据结构,它遵循先进先出(FIFO,First In First Out)的原则,即最先进入队列的元素最先被取出。队列通常支持两种基本操作:入队(enqueue),即向队列尾部添加元素;出队(dequeue),即从队列头部移除元素。其他常见的操作还包括获取队列头部元素(peek)和判断队列是否为空(isEmpty)。

        

        也就是说队列在插入数据的时候是在队尾进行插入,而在弹出数据的时候是在队头进行弹出。那么在实现队列的时候,数组和链表均可实现队列操作使⽤链表的结构实现更优⼀些,因为如果使⽤数组的结构,出队列在数组头上出数据,时间复杂度为O(N),效率会⽐较低,更推荐使用链表的形式。

        队列的实现:

                  队列的形式:

                        

 第一步:还是先typedef int 命名为QDataType类型节约后续维护成本。

 第二步:先定义队列节点结构,队列的底层为链表形式,所以在节点里定义一个next指针以及data数据。

  第三步:定义队列结构,QNode*head里存放着链表的头节点地址,QNode*tail里存放着链表的尾节点地址,最后定义一个size变量用于表示的队列的长度。

                队列的初始化以及销毁:

                        初始化队列:

  

第一步:

先对传进的队列结构体指针进行断言,如果指针为NULL,则进行报错;

第二步:

将队列的头指针与尾指针先初始化为NULL,并将size置为0;

       

                        销毁队列:

第一步:

先对传进的指针进行断言

第二步:

定义一个cur指针用来指向当前队列的头节点,由于队列的底层是顺序表,所以在销毁的时候需要单个节点依次销毁,使用while进行销毁

第三步:

重新将头节点以及尾节点置为NULL,防止野指针,并重新将size置为0;

                  入队:

第一步:

先对传进的指针进行断言,判断是否为NULL;

第二步:

申请新的节点,并将要插入的数据给newnode的值,并将newnode的next指针指向空。

第三步:

先对头指针进行判断,如果头指针为空,说明此时链表里没有节点,直接将头节点与尾节点指向新申请的节点。如果有节点,就将尾节点的next指针指向新节点,并重新将tail地址赋值为tail->next用来下次插入数据时,能快速找到尾节点,并同时对size++。

                  出队:

第一步:

        将pq指针以及pq->head指针进行断言,如果有一个为空进行报错

       接下来将size进行断言,如果size为0,表示队列里没有数据,无法进行删除操作,进行报错。

第二步:

        判断头节点的next指针是否为空,表示只有一个节点的话需要进行特殊处理,直接将头节点释放,并将头节点与尾巴节点同时置为NULL。

        如果是多个节点的话,由于这里是队列形式,队列想出数据只能从头部弹出,所以定义cur指针保存头节点->next节点,接着将头节点释放,并让头节点重新指向cur节点,最后将size--。

                  返回队列的长度以及判断队列是否为空:

                

                  返回队列头数据及返回队列尾数据:

                   

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 23:52:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 23:52:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 23:52:03       45 阅读
  4. Python语言-面向对象

    2024-07-21 23:52:03       55 阅读

热门阅读

  1. C#各种锁知识点

    2024-07-21 23:52:03       17 阅读
  2. Python之后端Django(四)

    2024-07-21 23:52:03       17 阅读
  3. n4.Nginx 核心模块

    2024-07-21 23:52:03       14 阅读
  4. android audio 相机按键音:(二)加载与修改

    2024-07-21 23:52:03       19 阅读
  5. 数字转换(树形DP)

    2024-07-21 23:52:03       18 阅读
  6. python 爬虫技术 第03节 基础复习 控制结构

    2024-07-21 23:52:03       17 阅读
  7. Python 模块导入方式

    2024-07-21 23:52:03       19 阅读
  8. 基于最新版的flutter pointycastle: ^3.9.1的AES加密

    2024-07-21 23:52:03       15 阅读
  9. Shiro-550反序列化漏洞

    2024-07-21 23:52:03       15 阅读