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

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



一、队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出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);
}
相关文章
|
7月前
|
缓存 算法 调度
数据结构入门 — 队列
本文属于数据结构专栏文章,适合数据结构入门者学习,涵盖数据结构基础的知识和内容体系,文章在介绍数据结构时会配合上动图演示,方便初学者在学习数据结构时理解和学习
55 0
|
6月前
|
算法 调度
数据结构入门:队列
数据结构入门:队列
34 0
|
1月前
|
机器学习/深度学习 存储 缓存
数据结构从入门到精通——队列
队列是一种特殊的线性数据结构,遵循先入先出(FIFO)的原则。它只允许在队列的末尾添加元素(称为入队操作),并从队列的开头移除元素(称为出队操作)。队列在多种应用中发挥着重要作用,如计算机系统的任务调度、打印机作业管理以及多线程编程中的线程同步等。
51 0
|
3月前
【数据结构】栈和队列(队列的基本操作和基础知识)
【数据结构】栈和队列(队列的基本操作和基础知识)
31 1
|
8月前
|
算法
数据结构学习之队列
数据结构学习之队列
50 0
|
9月前
|
存储 缓存 算法
数据结构之栈、队列——算法与数据结构入门笔记(四)
数据结构之栈、队列——算法与数据结构入门笔记(四)
|
4月前
|
存储 前端开发
手写数据结构 队列篇
手写数据结构 队列篇
33 0
|
8月前
|
Java
用Java实现简单的队列数据结构
用Java实现简单的队列数据结构
54 0
用Java实现简单的队列数据结构
【学习笔记之数据结构】队列
【学习笔记之数据结构】队列
31 0
数据结构26-优先级队列封装 原创
数据结构26-优先级队列封装 原创
30 0
数据结构26-优先级队列封装 原创