数据结构之队列的实现(附源码)

简介: 数据结构之队列的实现(附源码)



一、队列的概念及结构

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

二、队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

具体代码如下(C语言实现):

#pragma once
//Queue.h
// 链式结构:表示队列
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int QDateType;
typedef struct QListNode
{
  struct QListNode* _next;
  QDateType _data;
}QNode;
// 队列的结构 
typedef struct Queue
{
  QNode* _front;//队头
  QNode* _rear;//队尾
  int size;
}Queue;
// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDateType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDateType QueueFront(Queue* q);
// 获取队列队尾元素 
QDateType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);
//Queue.c
#include "Queue.h"
void QueueInit(Queue* q)
{
  assert(q);
  q->_front = NULL;
  q->_rear = NULL;
  q->size = 0;
}
void QueuePush(Queue* q, QDateType data)
{
  assert(q);
  QNode* tmp = (QNode*)malloc(sizeof(QNode));
  if (tmp == NULL)
  {
    perror("tmp malloc");
    exit(-1);
  }
  tmp->_data = data;
  tmp->_next = NULL;
  if (q->_rear == NULL)
  {
    q->_front = q->_rear = tmp;
  }
  else
  {
    q->_rear->_next = tmp;
    q->_rear = tmp;
  }
  q->size++;
}
void QueuePop(Queue* q)
{
  assert(q);
  assert(QueueEmpty(q));
  if (q->_front->_next == NULL)
  {
    free(q->_front);
    q->_front = q->_rear = NULL;
  }
  else
  {
    QNode* next = q->_front->_next;
    free(q->_front);
    q->_front = next;
  }
  q->size--;
}
QDateType QueueFront(Queue* q)
{
  assert(q);
  assert(QueueEmpty(q));
  return q->_front->_data;
}
QDateType QueueBack(Queue* q)
{
  assert(q);
  assert(QueueEmpty(q));
  return q->_rear->_data;
}
int QueueSize(Queue* q)
{
  assert(q);
  return q->size;
}
int QueueEmpty(Queue* q)
{
  assert(q);
  return q->size;
}
void QueueDestroy(Queue* q)
{
  assert(q);
  QNode* cur = q->_front;
  while (cur)
  {
    Queue* next = cur->_next;
    free(cur);
    cur = next;
  }
  q->_front = q->_rear = NULL;
  q->size = 0;
}
//test.c
#include "Queue.h"
void test02()
{
  Queue q1;
  QueueInit(&q1);
  QueuePush(&q1, 1);
  QueuePush(&q1, 2);
  QueuePush(&q1, 3);
  QueuePush(&q1, 4);
  QueuePush(&q1, 5);
  QueuePop(&q1);
  while (QueueEmpty(&q1))
  {
    printf("%d ", QueueFront(&q1));
    QueuePop(&q1);
  }
  printf("\n");
  QueueDestroy(&q1);
}
int main()
{
  test02();
  return 0;
}

拓展:循环队列

如上图所示:循环队列的节点数一般会比要求的节点数多一个,以便区分空的循环队列和满的循环队列。空的循环队列front和rear指向同一个节点,满的循环队列可以理解为rear = front + 1。

三、初学的队列以及栈和队列结合的练习题

题目一:设计循环队列

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。
typedef struct 
{
    int* a;
    int front;
    int rear;
    int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a = (int*)malloc(sizeof(int)*(k+1));
    obj->front = obj->rear = 0;
    obj->k = k;
    return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    //front和rear相等即为空
    return obj->front == obj->rear;    
}
bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    //数学方法判满
    return (obj->rear + 1) % (obj->k + 1) == obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    if(myCircularQueueIsFull(obj))
    return false;
    obj->a[obj->rear] = value;
    ++obj->rear;
    //不然rear可能越界
    obj->rear %= (obj->k+1);
    return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    return false;
    obj->front++;
    obj->front %= (obj->k+1);
    return true; 
}
int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    return -1;
    else
    return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    return -1;
    else
    //数学方法
    return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
}
void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->a);
    free(obj);
}

题目二:用队列实现栈

思路:用两个队列,保持一个队列为空一个不为空,当要出栈的时候就将不为空的队列中除了队尾的元素全都入到为空的队列中,然后将唯一的那个元素出栈。

typedef int QDateType;
typedef struct QListNode
{
  struct QListNode* _next;
  QDateType _data;
}QNode;
// 队列的结构 
typedef struct Queue
{
  QNode* _front;
  QNode* _rear;
  int size;
}Queue;
void QueueInit(Queue* q)
{
  assert(q);
  q->_front = NULL;
  q->_rear = NULL;
  q->size = 0;
}
void QueuePush(Queue* q, QDateType data)
{
  assert(q);
  QNode* tmp = (QNode*)malloc(sizeof(QNode));
  if (tmp == NULL)
  {
    perror("tmp malloc");
    exit(-1);
  }
  tmp->_data = data;
  tmp->_next = NULL;
  if (q->_rear == NULL)
  {
    q->_front = q->_rear = tmp;
  }
  else
  {
    q->_rear->_next = tmp;
    q->_rear = tmp;
  }
  q->size++;
}
void QueuePop(Queue* q)
{
  assert(q);
  assert(QueueEmpty(q));
  if (q->_front->_next == NULL)
  {
    free(q->_front);
    q->_front = q->_rear = NULL;
  }
  else
  {
    QNode* next = q->_front->_next;
    free(q->_front);
    q->_front = next;
  }
  q->size--;
}
QDateType QueueFront(Queue* q)
{
  assert(q);
  assert(QueueEmpty(q));
  return q->_front->_data;
}
QDateType QueueBack(Queue* q)
{
  assert(q);
  assert(QueueEmpty(q));
  return q->_rear->_data;
}
int QueueSize(Queue* q)
{
  assert(q);
  return q->size;
}
int QueueEmpty(Queue* q)
{
  assert(q);
  return q->size;
}
void QueueDestroy(Queue* q)
{
  assert(q);
  QNode* cur = q->_front;
  while (cur)
  {
    Queue* next = cur->_next;
    free(cur);
    cur = next;
  }
  q->_front = q->_rear = NULL;
  q->size = 0;
}
typedef struct 
{
    Queue q1;
    Queue q2;
} MyStack;
MyStack* myStackCreate() 
{
    MyStack* tmp = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&tmp->q1);
    QueueInit(&tmp->q2);
    return tmp;
}
void myStackPush(MyStack* obj, int x) 
{
    if(QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1, x);
    }
    else
    {
        QueuePush(&obj->q2, x);
    }
}
int myStackPop(MyStack* obj) 
{
    Queue* empty = &obj->q1;
    Queue* noempty = &obj->q2;
    if(QueueEmpty(&obj->q1))
    {
        empty = &obj->q2;
        noempty = &obj->q1;
    }
    while(QueueSize(noempty) > 1)
    {
        QueuePush(empty, QueueFront(noempty));
        QueuePop(noempty);
    }
    int top = QueueFront(noempty);
    QueuePop(noempty);
    return top;
}
int myStackTop(MyStack* obj) 
{
    if(QueueEmpty(&obj->q1))
    return QueueBack(&obj->q1);
    else
    return QueueBack(&obj->q2);
}
bool myStackEmpty(MyStack* obj) 
{
    int ret1, ret2;
    if(QueueEmpty(&obj->q1) == 0)
    ret1 = 0;
    else
    ret1 = 1;
    if(QueueEmpty(&obj->q2) == 0)
    ret2 = 0;
    else
    ret2 = 1;
    if(ret1 == 0 && ret2 == 0)
    return true;
    else
    return false;
}
void myStackFree(MyStack* obj) 
{
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

题目三:用栈实现队列

思路:使用两个栈,一个push栈一个pop栈,先往push栈中堆入元素,出队时,先将push栈中的元素先pop到pop栈中,再从pop栈中pop元素,其间再有元素堆入入到push栈中。

typedef int STDataType;
typedef struct Stack
{
  STDataType* _a;
  int _top;   // 栈顶
  int _capacity;  // 容量 
}Stack;
void StackInit(Stack* ps)
{
  assert(ps);
  ps->_a = NULL;
  ps->_top = 0;
  ps->_capacity = 0;
}
void StackPush(Stack* ps, STDataType data)
{
  assert(ps);
  if (ps->_capacity == ps->_top)
  {
    int newCapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
    STDataType* tmp = (STDataType*)realloc(ps->_a,sizeof(STDataType) * newCapacity);
    if (tmp == NULL)
    {
      perror("realloc fail");
      exit(-1);
    }
    ps->_a = tmp;
    ps->_capacity = newCapacity;
  }
  ps->_a[ps->_top] = data;
  ps->_top++;
}
void StackPop(Stack* ps)
{
  assert(ps);
  assert(ps->_top > 0);
  ps->_top--;
}
STDataType StackTop(Stack* ps)
{
  assert(ps);
  assert(ps->_top > 0);
  return ps->_a[ps->_top - 1];
}
int StackSize(Stack* ps)
{
  assert(ps);
  return ps->_top;
}
int StackEmpty(Stack* ps)
{
  return ps->_top;
}
void StackDestroy(Stack* ps)
{
  assert(ps);
  free(ps->_a);
  ps->_a = NULL;
  ps->_capacity = 0;
  ps->_top = 0;
}
typedef struct 
{
    Stack pushst;
    Stack popst;
} MyQueue;
MyQueue* myQueueCreate() 
{
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&obj->pushst);
    StackInit(&obj->popst);
    return obj;
}
void myQueuePush(MyQueue* obj, int x) 
{
    StackPush(&obj->pushst, x);
}
int myQueuePeek(MyQueue* obj) 
{
    //pop栈为空就往其中堆入元素
    if(StackEmpty(&obj->popst) == 0)
    {
        while(StackEmpty(&obj->pushst) > 0)
        {
            StackPush(&obj->popst, StackTop(&obj->pushst));
            StackPop(&obj->pushst);
        }
    }
    return StackTop(&obj->popst);
}
int myQueuePop(MyQueue* obj) 
{
    int front = myQueuePeek(obj);
    StackPop(&obj->popst);
    return front;
}
bool myQueueEmpty(MyQueue* obj) 
{
    int ret1, ret2;
    if(StackEmpty(&obj->pushst) == 0)
    ret1 = 0;
    else
    ret1 = 1;
    if(StackEmpty(&obj->popst) == 0)
    ret2 = 0;
    else
    ret2 = 1;
    if(ret1 == 0 && ret2 == 0)
    return true;
    else
    return false; 
}
void myQueueFree(MyQueue* obj) 
{
    StackDestroy(&obj->pushst);
    StackDestroy(&obj->popst);
    free(obj);
}
相关文章
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
609 1
|
11月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
508 3
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
970 77
|
11月前
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
914 19
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
540 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
数据库 C++
【数据结构进阶】红黑树超详解 + 实现(附源码)
本文深入探讨了红黑树的实现原理与特性。红黑树是一种自平衡二叉搜索树,通过节点着色(红/黑)和特定规则,确保树的高度接近平衡,从而实现高效的插入、删除和查找操作。相比AVL树,红黑树允许一定程度的不平衡,减少了旋转调整次数,提升了动态操作性能。文章详细解析了红黑树的性质、插入时的平衡调整(变色与旋转)、查找逻辑以及合法性检查,并提供了完整的C++代码实现。红黑树在操作系统和数据库中广泛应用,其设计兼顾效率与复杂性的平衡。
3482 3
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
354 11
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
334 9

热门文章

最新文章