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

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

二.队列

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天前
|
存储 算法 Java
Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性
Java Set因其“无重复”特性在集合框架中独树一帜。本文解析了Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性,并提供了最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的hashCode()与equals()方法。
17 4
|
5天前
数据结构(栈与列队)
数据结构(栈与列队)
11 1
|
10天前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
37 1
|
6天前
【数据结构】-- 栈和队列
【数据结构】-- 栈和队列
9 0
|
11天前
探索数据结构:队列的的实现与应用
探索数据结构:队列的的实现与应用
|
11天前
探索顺序结构:栈的实现方式
探索顺序结构:栈的实现方式
|
11天前
|
存储 C语言
栈和队列题目练习
栈和队列题目练习
12 0
|
18天前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
84 64
|
11天前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
16 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
11天前
初步认识栈和队列
初步认识栈和队列
36 10