【数据结构】带你深入栈和队列,轻松实现各种接口功能

简介: 【数据结构】带你深入栈和队列,轻松实现各种接口功能

二.队列

1.队列的概念及结构

  • 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)
  • 入队列:进行插入操作的一端称为队尾
    出队列:进行删除操作的一端称为队头

2.队列的实现

  • 队列也可以使用数组和链表的结构实现,实际上使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
  • 因此在下面的讲解中,我们通过链表来实现

队列的基本结构

typedef int QDataType;
typedef struct QueueNode
{
  struct QueueNode* next;
  QDataType data;
}QNode;
typedef struct Queue
{
  QNode* head;
  QNode* tail;
  int size;
}Que;
  • 在队列的实现中与之前最大的不同是我们使用了两个结构体,其中第一个保存链表的存储的元素以及地址,而第二个保存的则是队列的头和尾的指针,这样有什么意义呢?
  • 在队列中,我们常常需要获取队列保存元素的多少,同时我们又需要实现队列的先进先出,也就是尾插进,头插出,这样如果我们没有一个能够指向尾部的指针,每次都需要通过遍历一下我们这个链表队列来找尾,这无疑不利于程序运行,因此我们定义第二个结构体来找尾。

队列的初始化 QueueInit

//初始化队列
void QueueInit(Que* pq)
{
    assert(pq);
    pq->head = pq->tail = NULL;
    pq->size = 0;
}
  • 初始化非常简单啊,无法是指针置空,让队列元素size置0就行,不多解释啦

往队列中插入元素 QueuePush

//把结点插入队列中(尾插)
void QueuePush(Que* pq, QDataType x)
{
    assert(pq);
    QNode* newnode = (QNode*)malloc(sizeof(QNode));//malloc申请空间
    if (newnode == NULL)//判断申请是否成功
    {
        perror("malloc failed\n");
        exit(-1);
    }
    newnode->data = x;//插入元素
    newnode->next = NULL;
    if (pq->tail == NULL)
    {
        pq->head = pq->tail = newnode;//如果此时队列中没有元素就把头和尾都指向开辟出来的空间
    }
    else
    {
      //尾插
        pq->tail->next = newnode;
        pq->tail = newnode;
    }
    pq->size++;//插入元素成功,有效元素个数加1
}

  • 上面这个图很形象的画出了入队和出队的情况,就不像之前详细的每一过程都用逻辑图带大家逐一分析了,大家结合这张图以及代码中的注释我相信非常好理解这个部分。

删除队列中的元素 QueuePop

// 删除队列中某个结点
void QueuePop(Que* pq)
{
    assert(pq);
    assert(!QueueEmpty(pq));//判空,如果为空就不需要再删除了
    //如果只有一个结点
    if (pq->head->next == NULL)
    { //只有一个结点,释放该节点将头尾置空
        free(pq->head);
        pq->head = pq->tail = NULL;
    }
    else
    { //出队列
        QNode* next = pq->head->next;//保存一下此时要删除结点的下一个结点      
        free(pq->head);//释放掉该结点
        pq->head = next;//头指向删除结点的下一个结点
    }
    pq->size--;//队列中有效元素减1
}

判断队列是否为空 QueueEmpty

//判断队列是否为空
bool QueueEmpty(Que* pq)
{
    assert(pq);
    return pq->tail == NULL;
}
  • 这里利用返回bool的函数来判断我们的尾是否为0,为0说明此时的队列中没有元素,队列为空返回true,如果尾指针不为0说明队列不为空就返回false

获取队列中的队头元素 QueueFront

//获取队列的队头元素
QDataType QueueFront(Que* pq)
{
    assert(pq);
    assert(!QueueEmpty(pq));
    QNode* cur = pq->head;
    return cur->data;
}
  • 队头元素就是头指针指向的结点,返回头指针存储的元素即可

获取队尾的元素 QueueBack

//获取队列的队尾元素
QDataType QueueBack(Que* pq)
{
    assert(pq);
    assert(!QueueEmpty(pq));
    return pq->tail->data;
}
  • 这里就体现了我们使用两个结构体的优势,我们通过尾指针就可以直接指向队尾,返回队尾的元素即可

队列的销毁 QueueDestroy

//队列的销毁
void QueueDestroy(Que* pq)
{
    assert(pq);
    QNode* cur = pq->head;//保存头指针
    while (cur)
    {
        QNode* next = cur->next;//保存需要销毁结点的下一个结点,防止释放掉后找不到
        free(cur);
        cur = next;
    }
    pq->head = pq->tail = NULL;//头尾指针全置空
    pq->size = 0;//有效元素置0
}
  • 我们通过遍历队列链表中每一个结点来销毁我们的队列,当head为空时,说明我们链表中的结点已经全部被销毁,队列的销毁完成

计算队列元素的数量

//计算队列的元素的数量
int QueueSize(Que* pq)
{
    return pq->size;
}
  • 我们每次进行入队或者出队时,都会让有效元素size+1或者-1,因此此时需要获取队列元素的数量时,返回size即可。

总结

  • 今天的内容到这里就结束了,栈与队列的最大区别在于栈是后进先出,而队列是先进先出,两者在实现中实际都并没有那么难,如果你想学好这部分内容的话,不妨自己照着上面的内容试试吧!
  • 好了,如果你有任何疑问欢迎在评论区或者私信我提出,大家下次再见啦!
目录
相关文章
|
4天前
栈的几个经典应用,真的绝了
文章总结了栈的几个经典应用场景,包括使用两个栈来实现队列的功能以及利用栈进行对称匹配,并通过LeetCode上的题目示例展示了栈在实际问题中的应用。
栈的几个经典应用,真的绝了
|
1天前
|
负载均衡 网络协议 安全
DKDP用户态协议栈-kni
DKDP用户态协议栈-kni
|
1天前
|
负载均衡 网络协议 安全
DPDK用户态协议栈-KNI
DPDK用户态协议栈-KNI
|
1天前
|
测试技术
【初阶数据结构篇】栈的实现(附源码)
在每一个方法的第一排都使用assert宏来判断ps是否为空(避免使用时传入空指针,后续解引用都会报错)。
|
1天前
|
测试技术
【初阶数据结构篇】队列的实现(赋源码)
首先队列和栈一样,不能进行遍历和随机访问,必须将队头出数据才能访问下一个,这样遍历求个数是不规范的。
|
4天前
|
算法
【数据结构与算法】优先级队列
【数据结构与算法】优先级队列
6 0
|
4天前
|
存储 算法
【数据结构与算法】队列(顺序存储)
【数据结构与算法】队列(顺序存储)
5 0
【数据结构】栈和队列
【数据结构】栈和队列
|
6天前
|
算法 C语言 C++
【practise】栈的压入和弹出序列
【practise】栈的压入和弹出序列
|
6天前
|
C语言
用栈实现将一个十进制数值转换成八进制数值。即用该十进制数值除以8,并保留其余数;重复此操作,直到该十进制数值为0为止。最后将所有的余数反向输出就是所对应的八进制数值
这篇文章展示了如何使用栈(包括顺序栈和链栈)实现将十进制数值转换成八进制数值的方法,通过C语言编程演示了两种栈的实现方式和使用场景。
用栈实现将一个十进制数值转换成八进制数值。即用该十进制数值除以8,并保留其余数;重复此操作,直到该十进制数值为0为止。最后将所有的余数反向输出就是所对应的八进制数值