5. 队列 - 队列存储结构及算法实现

5. 队列 - 队列存储结构及算法实现

5.1 队列 - 基本概念

  • 队列是两端都开口"的线性储存结构,要求数据只能从一端进,从另一端出,遵循 "先进先出" 的原则。
  • 队列存储结构的实现有以下两种方式:
    • 顺序队列:在顺序表的基础上实现的队列结构。
    • 链队列:在链表的基础上实现的队列结构。

5.2 队列 - 顺序表实现队列存储结构

  • 顺序队列的底层使用的是数组,因此需预先申请一块足够大的内存空间初始化顺序队列。为了满足顺序队列中数据从队尾进,队头出且先进先出的要求,我们还需要定义两个指针(top 和 rear)分别用于指向顺序队列中的队头元素和队尾元素
  • 如果数据在数组中位置只向后移动,整个顺序队列在数据不断地进队出队过程中,在顺序表中的位置不断后移,存储区会不停的扩大,因此实际中存储数组循环存储,相当于存储区域是一个环形结构
  • 顺序表实现队列存储结构如下:
#include <stdio.h>
#include <stdlib.h>
#include<stdint.h>

typedef struct QueueDefine {
    uint8_t* QueueAdress;     // 存储地址
    uint16_t top;             // 队头
    uint16_t rear;            // 队尾
    uint16_t size;            // 队列容量
    uint8_t  FullOrEmptyflag; //解决取存中top和rear重合时为空或者为满的情况,1为满
}Queue;
/**
* Discribe : 创建一个队列
* Input : size -> 创建队列的容量尺寸
* return : 申请队列的地址
**/
Queue CreateQueue(uint16_t size) {
    Queue QueueCreate;
    QueueCreate.QueueAdress = (uint8_t*)malloc(size * sizeof(uint8_t));
    memset(QueueCreate.QueueAdress, 0xff, size);
    QueueCreate.top = 0;
    QueueCreate.rear = 0;
    QueueCreate.size = size;
    QueueCreate.FullOrEmptyflag = 0;
    return QueueCreate;
}
/**
* Discribe : 判断队列是否为空或者满
* Input : queue -> 存储队列
* return : 1 -> 队列为满 , 0 -> 队列为空
**/
uint8_t QueueIsEmptyOrFull(Queue* queue) {
    return queue->FullOrEmptyflag;
}
/**
* Input : queue->存储队列
* return : 队列可存储空间
* */
uint16_t GetQueueSpace(Queue* queue) {
    if (queue->top == queue->rear) {
        return QueueIsEmptyOrFull(queue) ? 0 : queue->size ;
    }
    if (queue->top < queue->rear) {
        return queue->size - (queue->rear - queue->top );
    }else {
        return queue->top - queue->rear;
    }
}
/**
* Discribe : 数据入队
* Input : queue -> 存储队列, data -> 入队数据, length -> 入队长度
* return : 1 -> 入队成功 , 0 -> 入队失败
**/
uint8_t EnterQueue(Queue* queue, uint8_t* data, uint8_t length) {
    if (data == NULL || length < 1) {
        printf("入队数据有误!!!\n");
        return 0;
    }
    if (GetQueueSpace(queue) < length) {
        printf("队列空间不足, space = %d, data length = %d!!!\n", GetQueueSpace(queue), length);
        return 0;
    }
    for (uint16_t i = 0; i < length; i++) {
        queue->QueueAdress[queue->rear % queue->size] = data[i];
        queue->rear = ((queue->rear + 1) % queue->size);
        if (queue->top == queue->rear) {
            queue->FullOrEmptyflag = 1;
        }
    }
}

/**
* Discribe : 数据出队
* Input : queue -> 存储队列, data -> 出队存放地址, length -> 出队长度
* return : 1 -> 出队成功 , 0 -> 出队失败
**/
uint8_t  LeaveQueue(Queue* queue, uint8_t* data, uint8_t length) {
    if (data == NULL || length < 1) {
        printf("出队数据指令有误!!!\n");
        return 0;
    }
    if ((queue->size - GetQueueSpace(queue)) < length) {
        printf("取出数据长度大于队列存储数据长度, save size = %d, data length = %d!!!\n", queue->size - GetQueueSpace(queue), length);
        return 0;
    }
    for (uint16_t i = 0; i < length; i++) {
        data[i] = queue->QueueAdress[queue->top % queue->size];
        queue->QueueAdress[queue->top % queue->size] = 0xff;
        queue->top = ((queue->top + 1 ) % queue->size);
        if (queue->top == queue->rear) {
            queue->FullOrEmptyflag = 0;
        }
    }
}

void display(Queue* queue) {
    printf("队列空间存储内容为(ff无效值):");
    for (uint16_t i = 0; i < queue->size; i++) {
        printf("%x ", queue->QueueAdress[i]);
    }
    printf("[ top = %d , rear = %d ]", queue->top, queue->rear);
    printf(" 队列空间 space = %d !\n\n", GetQueueSpace(queue));
}

int main() {
    Queue quece = CreateQueue(10);
    printf("创建了一个容量为 :%d 的队列!\n", quece.size);
    display(&quece);
    uint8_t data[10] = {0};
    for (uint16_t i = 0; i < 10; i++) {
        data[i] = i + 1;
    }
    EnterQueue(&quece, data, 2);
    display(&quece);
    EnterQueue(&quece, &data[2], 8);
    display(&quece);
    uint8_t getdata[10] = { 0 };
    LeaveQueue(&quece, getdata, 3);
    display(&quece);
    LeaveQueue(&quece, getdata, 7);
    display(&quece);
    EnterQueue(&quece, data, 5);
    display(&quece);
    return 0;
}

在存取时会遇到top和rear重合的情况,如下示意图所示,因此上述方法中引入一个存储标志来解决取存中top和rear重合时为空或者为满的情况,当入队时遇到重合,则只可能为满,同理出队遇到重合为空
在这里插入图片描述

  • 直接用数组和指针实现方法
#include <stdio.h>
#include <stdlib.h>
#include<stdint.h>
#define BUFFER_LENGTH 2048
typedef unsigned char BOOL;
#define TRUE 1
#define FALSE 0
void FifoQueueInit();
BOOL FifoQueueIsEmpty();
BOOL FifoQueueIsFull();
int GetFifoQueueSize();
int GetFifoQueueSpace();
int FifoQueueInster(unsigned char Datas[], int nSize);
int FifoQueuePopup(unsigned char Datas[], int nSize);

static unsigned char FifoQueueBuffer[BUFFER_LENGTH + 1];  // 存储队列
static unsigned char* pFifoQueueHead = FifoQueueBuffer;   // 头指针
static unsigned char* pFifoQueueTail = FifoQueueBuffer;   // 尾指针

void FifoQueueInit()   // 初始化队列
{
    memset(FifoQueueBuffer, 0x00, BUFFER_LENGTH + 1);
    pFifoQueueHead = FifoQueueBuffer;
    pFifoQueueTail = FifoQueueBuffer;
}

BOOL FifoQueueIsEmpty()
{
    // 头尾指针指向同一个位置,则队列为空
    return (pFifoQueueHead == pFifoQueueTail) ? TRUE : FALSE;
}

BOOL FifoQueueIsFull()
{
    if (pFifoQueueTail == FifoQueueBuffer)   
    {   // 尾指针指向数组[0]位置
        return ((pFifoQueueHead - pFifoQueueTail) == BUFFER_LENGTH) ? TRUE : FALSE;
    }
    else
    {   // 头执政在前,尾指针紧随其后 
        return (pFifoQueueHead == pFifoQueueTail - 1) ? TRUE : FALSE;
    }
}

int GetFifoQueueSize()   // 已存长度
{
    if (pFifoQueueHead >= pFifoQueueTail)
    {   // 头指针在前
        return pFifoQueueHead - pFifoQueueTail;
    }
    else
    {   // 头指针在后
        return (BUFFER_LENGTH - (pFifoQueueTail - pFifoQueueHead - 1));
    }
}

int GetFifoQueueSpace()   // 剩余可存空间
{
    return (BUFFER_LENGTH - GetFifoQueueSize());
}

int FifoQueueInster(unsigned char Datas[], int nSize)
{
    int n = 0; // 统计存入数据长度
    for (int i = 0; i < nSize; i++)
    {
        if (FifoQueueIsFull())  // 如果存储已满,停止存储
        {
            break;
        }
        (*pFifoQueueHead) = Datas[i];
        if (pFifoQueueHead == FifoQueueBuffer + BUFFER_LENGTH) {
            pFifoQueueHead = FifoQueueBuffer;
        }
        else {
            pFifoQueueHead++;
        }
        n++;
    }
    return n;
}

int FifoQueuePopup(unsigned char Datas[], int nSize)
{
    int i = 0; 
    int n = 0; 
    for (i = 0; i < nSize; i++)
    {
        if (FifoQueueIsEmpty()) 
        {
            break;
        }
        Datas[i] = (*pFifoQueueTail);
        if (pFifoQueueTail == FifoQueueBuffer + BUFFER_LENGTH)
        {
            pFifoQueueTail = FifoQueueBuffer;
        }
        else
        {
            pFifoQueueTail++;
        }
        n++;
    }
    return n;
}

5.3 队列 - 链表实现队列存储结构

  • 链式是实时申请空间,因此链式队列就不需要考虑空间利用的问题
  • 链表实现队列存储结构如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct QNode{
    int data;
    struct QNode * next;
}QNode;
QNode * initQueue(){
    QNode * queue = (QNode*)malloc(sizeof(QNode));
    queue->next = NULL;
    return queue;
}
QNode* enQueue(QNode * rear,int data){
    QNode * enElem = (QNode*)malloc(sizeof(QNode));
    enElem->data = data;
    enElem->next = NULL;
    // 使用尾插法向链队列中添加数据元素
    rear->next = enElem;
    rear = enElem;
    return rear;
}
QNode* DeQueue(QNode * top,QNode * rear){
    if (top->next == NULL) {
        printf("队列为空!!!\n");
        return rear;
    }
    QNode * p = top->next;
    printf("%d ", p->data);
    top->next = p->next;
    if (rear == p) {
        rear = top;
    }
    free(p);
    return rear;
}
int main() {
    QNode * queue,*top,*rear;
    queue = top = rear = initQueue();// 创建头结点
    // 向链队列中添加结点,使用尾插法添加的同时,队尾指针需要指向链表的最后一个元素
    rear = enQueue(rear, 1);
    rear = enQueue(rear, 2);
    rear = enQueue(rear, 3);
    rear = enQueue(rear, 4);
    // 入队完成,所有数据元素开始出队列
    rear = DeQueue(top, rear);
    rear = DeQueue(top, rear);
    rear = DeQueue(top, rear);
    rear = DeQueue(top, rear);
    rear = DeQueue(top, rear);
    return 0;
}

感谢阅读 若有错误 敬请见谅!!!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

园长QwQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值