数据结构06--链队列

1.基本概念

        队列是最常见的概念,日常生活经常需要排队,仔细观察队列会发现,队列是一种逻辑结构,是一种特殊的线性表。特殊在:

  • 只能在固定的两端操作线性表

只要满足上述条件,那么这种特殊的线性表就会呈现一种“先进先出”的逻辑,这种逻辑就被称为队列。

由于约定了只能在线性表固定的两端进行操作,于是给队列这种特殊的线性表的插入删除,起个特殊的名称:

  • 队头:可以删除节点的一端
  • 队尾:可以插入节点的一端
  • 入队:将节点插入到队尾之后,函数名通常为enQueue()
  • 出队:将队头节点从队列中剔除,函数名通常为outQueue()
  • 取队头:取得队头元素,但不出队,函数名通常为front()

由于这种固定两端操作的简单约定,队列获得了“先进先出”(FIFO)的基本特性,如下图所示:

2.链式队列操作:

链式队列(一个单向链表)的组织形式与链表无异,只不过插入删除被约束在固定的两端。为了便于操作,通常也会创建所谓管理结构体,用来存储队头指针、队尾指针、队列元素个数等信息:

从上图可以看到,链式队列主要控制队头和队尾,由于管理结构体中保存了当前队列元素个数size,因此可以不必设计链表的头节点,初始化空队列时只需要让队头队尾指针同时指向空即可。

2.1 节点设计:

以下是队列链表节点设计和管理结构体设计的示例代码:

// 链队列节点设计
typedef struct node
{
    int data;
    struct node *next;
} Node_t, *P_Node_t;

// 管理链队列结构体
typedef struct linkQueue
{
    int size;           // 链队节点个数
    struct node *front; // 队头指针
    struct node *rear;  // 队尾指针
} linkQueue;

2.2链队初始化:

/// @brief 管理链队结构体初始化
/// @return 返回新的链队管理结构体
linkQueue *queueInit()
{
    // 管理链队结构体申请堆内存空间
    linkQueue *q = (linkQueue *)calloc(1, sizeof(linkQueue));
    // 判断链队管理结构体堆内存空间是否申请成功
    if (q == NULL)
    {
        printf("管理链队结构体堆内存空间失败.\n");
        return NULL;
    }
    // 链队队头指针指向NULL
    q->front = NULL;
    // 链队对尾指针指向NULL
    q->rear = NULL;
    // 链队节点个数初始化为0
    q->size = 0;
    // 返回管理链队结构体指针
    return q;
}
/// @brief 链队节点初始化
/// @param newData 新数据
/// @return 返回新节点
P_Node_t nodeInit(int newData)
{
    // 申请新节点堆内存空间
    P_Node_t newNode = (P_Node_t)calloc(1, sizeof(Node_t));
    // 判断新节点堆内存空间是否申请成功
    if (newNode == NULL)
    {
        printf("新节点堆内存空间申请失败.\n");
        return NULL;
    }
    // 新节点数据初始化
    newNode->data = newData;
    // 新节点next指向NULL
    newNode->next = NULL;
    // 返回新节点
    return newNode;
}

2.3入队

/// @brief 新节点入队
/// @param q 链式队列管理结构体指针
/// @param newNode 新节点
/// @return 入队成功返回真,失败返回假
bool enQueue(linkQueue *q, P_Node_t newNode)
{
    // 判断队列或者新节点是否为NULL
    if (q == NULL || newNode == NULL)
    {
        printf("管理链队结构体或新节点为空.\n");
        return false;
    }
    // 链式队列入队要考虑到链队是否为空
    if (q->size == 0)
    {
        // 当链队为空时,队头队尾指针都指向新节点
        q->front = newNode;
        q->rear = newNode;
    }
    else
    {
        // 当链队不为空时
        // 队尾指针的next指针指向新节点
        q->rear->next = newNode;
        // 队尾指针指向新节点
        q->rear = newNode;
    }
    // 节点个数+1
    q->size++;
    // 节点入队返回真
    return true;
}

2.4出队

/// @brief 节点出队
/// @param q 管理链队结构体
/// @param outData 出队的节点数据
/// @return 出队成功返回真,失败返回假
bool outQueue(linkQueue *q, int *outData)
{
    // 当链队无节点出队时
    if (q->size == 0)
    {
        printf("链队为空.\n");
        return false;
    }
    // tmp指向队头节点,即要出队节点
    P_Node_t tmp = q->front;
    // 将出队节点数据赋给*outData
    *outData = tmp->data;
    // 队头指针指向下一个节点
    q->front = q->front->next;
    // 链队节点个数-1
    q->size--;
    // 当链队节点为0时
    if (q->size == 0)
    {
        // 链队的队头队尾指针指向NULL
        q->front = q->rear = NULL;
    }
    // 释放tmp出队节点
    free(tmp);
    // 出队成功返回真
    return true;
}

2.5遍历链队

/// @brief 遍历节点
/// @param q 管理链队结构体
void display(linkQueue *q)
{
    // q->size时链队为空
    if (q->size == 0)
    {
        printf("链队无数据.\n");
        return;
    }
    printf("当前队列为:\n");
    // tmp遍历链队
    for (P_Node_t tmp = q->front; tmp != NULL; tmp = tmp->next)
    {
        printf("%d\n", tmp->data);
    }
    return;
}

2.6 释放链队

    // 释放链队节点堆内存空间
    while (q->size != 0)
    {
        outQueue(q, &outData);
    }
    // 释放管理链队结构体
    free(q);

2.7代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// 链队列节点设计
typedef struct node
{
    int data;
    struct node *next;
} Node_t, *P_Node_t;
// 管理链队列结构体
typedef struct linkQueue
{
    int size;           // 链队节点个数
    struct node *front; // 队头指针
    struct node *rear;  // 队尾指针
} linkQueue;
/// @brief 管理链队结构体初始化
/// @return 返回新的链队管理结构体
linkQueue *queueInit()
{
    // 管理链队结构体申请堆内存空间
    linkQueue *q = (linkQueue *)calloc(1, sizeof(linkQueue));
    // 判断链队管理结构体堆内存空间是否申请成功
    if (q == NULL)
    {
        printf("管理链队结构体堆内存空间失败.\n");
        return NULL;
    }
    // 链队队头指针指向NULL
    q->front = NULL;
    // 链队对尾指针指向NULL
    q->rear = NULL;
    // 链队节点个数初始化为0
    q->size = 0;
    // 返回管理链队结构体指针
    return q;
}
/// @brief 链队节点初始化
/// @param newData 新数据
/// @return 返回新节点
P_Node_t nodeInit(int newData)
{
    // 申请新节点堆内存空间
    P_Node_t newNode = (P_Node_t)calloc(1, sizeof(Node_t));
    // 判断新节点堆内存空间是否申请成功
    if (newNode == NULL)
    {
        printf("新节点堆内存空间申请失败.\n");
        return NULL;
    }
    // 新节点数据初始化
    newNode->data = newData;
    // 新节点next指向NULL
    newNode->next = NULL;
    // 返回新节点
    return newNode;
}
/// @brief 新节点入队
/// @param q 链式队列管理结构体指针
/// @param newNode 新节点
/// @return 入队成功返回真,失败返回假
bool enQueue(linkQueue *q, P_Node_t newNode)
{
    // 判断队列或者新节点是否为NULL
    if (q == NULL || newNode == NULL)
    {
        printf("管理链队结构体或新节点为空.\n");
        return false;
    }
    // 链式队列入队要考虑到链队是否为空
    if (q->size == 0)
    {
        // 当链队为空时,队头队尾指针都指向新节点
        q->front = newNode;
        q->rear = newNode;
    }
    else
    {
        // 当链队不为空时
        // 队尾指针的next指针指向新节点
        q->rear->next = newNode;
        // 队尾指针指向新节点
        q->rear = newNode;
    }
    // 节点个数+1
    q->size++;
    // 节点入队返回真
    return true;
}
/// @brief 节点出队
/// @param q 管理链队结构体
/// @param outData 出队的节点数据
/// @return 出队成功返回真,失败返回假
bool outQueue(linkQueue *q, int *outData)
{
    // 当链队无节点出队时
    if (q->size == 0)
    {
        printf("链队为空.\n");
        return false;
    }
    // tmp指向队头节点,即要出队节点
    P_Node_t tmp = q->front;
    // 将出队节点数据赋给*outData
    *outData = tmp->data;
    // 队头指针指向下一个节点
    q->front = q->front->next;
    // 链队节点个数-1
    q->size--;
    // 当链队节点为0时
    if (q->size == 0)
    {
        // 链队的队头队尾指针指向NULL
        q->front = q->rear = NULL;
    }
    // 释放tmp出队节点
    free(tmp);
    // 出队成功返回真
    return true;
}
/// @brief 遍历节点
/// @param q 管理链队结构体
void display(linkQueue *q)
{
    // q->size时链队为空
    if (q->size == 0)
    {
        printf("链队无数据.\n");
        return;
    }
    printf("当前队列为:\n");
    // tmp遍历链队
    for (P_Node_t tmp = q->front; tmp != NULL; tmp = tmp->next)
    {
        printf("%d\t", tmp->data);
    }
    return;
}
int main(int argc, char const *argv[])
{
    // 初始化管理链队结构体
    linkQueue *q = queueInit();
    char optMenu;
    P_Node_t newNode = NULL;
    int data, outData = 0, overFlag = 0;
    while (1)
    {
        printf("输入i入队\t输入o出队\t输入d打印当前队列\tt.退出\n");
        scanf("%c", &optMenu);
        switch (optMenu)
        {
        case 'i':
            printf("请输入数据入队:\n");
            scanf("%d", &data);
            newNode = nodeInit(data);
            if (enQueue(q, newNode))
            {
                printf("入队成功.\n");
            }
            else
            {
                printf("入队失败.\n");
            }
            break;
        case 'o':
            if (outQueue(q, &outData))
            {
                printf("出队成功,出队的数据为:%d\n", outData);
            }
            else
            {
                printf("出队失败.\n");
            }
            break;
        case 'd':
            display(q);
            printf("\n");
            break;
        case 't':
            overFlag = 1;
            break;
        default:
            printf("输入错误,请重新输入.\n");
            break;
        }
        if (overFlag)
        {
            break;
        }
        while (getchar() != '\n')
            ;
    }
    // 释放链队节点堆内存空间
    while (q->size != 0)
    {
        outQueue(q, &outData);
    }
    // 释放管理链队结构体
    free(q);
    
    return 0;
}

3.结语

        链式队列是一种灵活且高效的数据结构,适用于各种需要动态存储管理和高效数据操作的场景。与数组实现的队列相比,链式队列在尺寸管理和内存利用上具有明显优势,能够动态地扩展或收缩,从而有效应对不确定的数据量。

        本文探讨了链式队列的基本结构、操作方法以及在实际应用中的优势。通过对链式队列的掌握,开发者可以更好地处理各种任务调度、资源管理等场景下的数据流动需求

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值