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

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



一、队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出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);
}
相关文章
|
27天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
123 9
|
26天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
63 16
|
26天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
83 8
|
28天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
54 4
|
1月前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
61 3
|
29天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
2月前
初步认识栈和队列
初步认识栈和队列
61 10
|
2月前
|
Java C++ 索引
让星星⭐月亮告诉你,LinkedList和ArrayList底层数据结构及方法源码说明
`LinkedList` 和 `ArrayList` 是 Java 中两种常见的列表实现。`LinkedList` 基于双向链表,适合频繁的插入和删除操作,但按索引访问元素效率较低。`ArrayList` 基于动态数组,支持快速随机访问,但在中间位置插入或删除元素时性能较差。两者均实现了 `List` 接口,`LinkedList` 还额外实现了 `Deque` 接口,提供了更多队列操作。
26 3
|
2月前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
23 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
28天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
41 0