基于计数器的循环队列实现及优化

一、队列的基本概念

队列(Queue)是一种遵循"先进先出"(FIFO, First In First Out)原则的线性数据结构。它在计算机科学领域有着广泛应用,例如:

  • 操作系统中的进程调度与任务排队
  • 网络通信中的数据包缓冲处理
  • 广度优先搜索(BFS)算法的实现
  • 打印机等外设的任务处理队列

队列的核心操作包括:

  • 入队(Enqueue):在队列尾部插入元素
  • 出队(Dequeue):从队列头部移除元素
  • 判空(IsEmpty):判断队列是否为空
  • 获取队头(GetFront):查看队列头部元素

二、基于计数器的循环队列实现

本次实现的循环队列通过引入计数器(size)来解决传统循环队列的空间浪费问题,下面我们详细解析代码实现:

#include <iostream>
#include <stdio.h>
using namespace std;

#define maxsize 10
typedef int elemtype;

typedef struct queue {
    elemtype data[maxsize];
    int front ,rear;
    int size;

}sq_queue;

1. 数据结构定义

  • maxsize:定义队列的最大容量为10
  • elemtype:元素类型别名,此处为int类型
  • sq_queue:队列结构体,包含三个成员:
    • data[maxsize]:存储队列元素的数组
    • front:队头指针,指向队头元素
    • rear:队尾指针,指向队尾元素的下一个位置
    • size:计数器,记录当前队列中的元素个数

关键改进:相比传统循环队列,本实现增加了size变量,用于直接记录队列中元素的数量

2. 队列初始化

void init_queue(sq_queue &q) {
    q.rear=q.front=0;
    q.size=0;
}

初始化操作做了三件事:

  • 将队头指针front置为0
  • 将队尾指针rear置为0
  • 将计数器size置为0,表示初始状态下队列为空

3. 队列判空操作

bool queueempty(sq_queue &q) {
    if (q.size==0) {
        cout<<"队列为空"<<endl;
        return true;
    }
    else
        cout<<"队列不空"<<endl;
    return false;
}

判空逻辑非常直观:

  • 当size为0时,队列为空,返回true
  • 否则队列非空,返回false
  • 同时输出相应的状态提示信息

4. 入队操作

void enqueue(sq_queue &q,elemtype e) {
    if (q.size==maxsize) {
        cout<<"队列已满,无法插入"<<endl;
        return;
    }

    q.data[q.rear]=e;
    q.rear=(q.rear+1)%maxsize;
    q.size++;
    cout<<"插入元素"<<e<<endl;
}

入队操作步骤:

  1. 检查队列是否已满:通过判断size是否等于maxsize
  2. 若队列未满,将元素e存入rear指向的位置
  3. 队尾指针rear循环后移(通过模运算实现循环特性)
  4. 计数器size加1,更新元素数量
  5. 输出插入成功的提示信息

5. 出队操作

void dequeue(sq_queue &q) {
    if (queueempty(q)) {
        return;
    }

    cout<<"删除元素为"<<q.front<<endl;  // 注意:此处应为q.data[q.front]
    q.front=(q.front+1)%maxsize;
    q.size--;
}

出队操作步骤:

  1. 先调用queueempty函数检查队列是否为空,若为空则直接返回
  2. 输出要删除的队头元素(注:原代码此处有笔误,应输出q.data[q.front])
  3. 队头指针front循环后移
  4. 计数器size减1,更新元素数量

6. 获取队头元素

elemtype gethead(sq_queue &q) {
    if (queueempty(q)) {
       return q.front;  // 队空时返回值不合理,建议返回特殊标记
    }
    else {
        return q.data[q.front];
    }
}

获取队头元素操作:

  1. 检查队列是否为空
  2. 若队列非空,返回front指针指向的元素
  3. 若队列为空,返回front值(此处设计不合理,建议返回-1等特殊标记)

7. 主函数测试

int main() {
    sq_queue q;

    // 测试初始化队列
    cout << "=== 初始化队列 ===" << endl;
    init_queue(q);

    // 测试队列是否为空
    cout << "\n=== 测试队列是否为空 ===" << endl;
    queueempty(q);

    // 测试入队操作
    cout << "\n=== 测试入队操作 ===" << endl;
    enqueue(q, 10);
    enqueue(q, 20);
    enqueue(q, 30);
    enqueue(q, 40);

    // 再次检查队列是否为空
    cout << "\n=== 再次检查队列是否为空 ===" << endl;
    queueempty(q);

    // 测试获取队头元素
    cout << "\n=== 测试获取队头元素 ===" << endl;
    cout << "当前队头元素为: " << gethead(q) << endl;

    // 测试出队操作
    cout << "\n=== 测试出队操作 ===" << endl;
    dequeue(q);
    dequeue(q);

    // 再次获取队头元素
    cout << "\n=== 再次获取队头元素 ===" << endl;
    cout << "当前队头元素为: " << gethead(q) << endl;

    // 测试队列满的情况
    cout << "\n=== 测试队列满的情况 ===" << endl;
    enqueue(q, 50);
    enqueue(q, 60);
    enqueue(q, 70);
    enqueue(q, 80);
    enqueue(q, 90);
    enqueue(q, 100);
    enqueue(q, 110);
    enqueue(q, 120);  // 尝试插入第10个元素(队列最大容量为10)

    // 测试连续出队直到队空
    cout << "\n=== 测试连续出队直到队空 ===" << endl;
    dequeue(q);
    dequeue(q);
    dequeue(q);
    dequeue(q);
    dequeue(q);
    dequeue(q);
    dequeue(q);
    dequeue(q);

    // 测试队空时出队
    cout << "\n=== 测试队空时出队 ===" << endl;
    dequeue(q);

    return 0;
}

主函数设计了完整的测试流程,涵盖了队列的各种基本操作和边界情况,包括:

  • 初始化队列
  • 队列判空
  • 入队操作
  • 获取队头元素
  • 出队操作
  • 队列满状态测试
  • 连续出队至空队列
  • 队空时的出队操作

三、基于计数器的循环队列的优缺点

优点:

  1. 空间利用率100%:相比传统循环队列,不需要牺牲一个存储空间来区分空满状态
  2. 判断逻辑简单:通过size直接判断队列空满,无需复杂的模运算判断
  3. 操作高效:所有操作(入队、出队、判空、获取队头)的时间复杂度均为O(1)
  4. 实现直观:通过计数器直观反映队列中元素数量,便于理解和维护
  5. 循环特性:保持了循环队列的优势,避免了普通顺序队列的"假溢出"问题

缺点:

  1. 需要额外存储空间:增加了一个int类型的size变量(在maxsize较大时影响可忽略)
  2. 容量固定:队列大小在初始化时确定,无法动态扩展
  3. 元素类型受限:需要预先定义元素类型,通用性不如模板实现
  4. 计数器维护成本:入队和出队时都需要手动更新size,增加了出错可能性

四、空间浪费问题的优化分析

传统循环队列通过(rear+1)%maxsize == front判断队列满,这种方式必须牺牲一个存储空间,导致实际可用容量为maxsize-1,存在1个单位的空间浪费。

本实现通过引入size计数器,从根本上解决了这个问题:

  • 队空条件:size == 0
  • 队满条件:size == maxsize

这种设计使得队列的实际可用容量达到maxsize,实现了100%的空间利用率。当maxsize较大时,这种优化能显著提升空间效率。

以代码中的maxsize=10为例:

  • 传统实现最多存储9个元素
  • 本实现可存储10个元素,充分利用了所有空间

空间效率的提升对于资源受限的环境(如嵌入式系统)尤为重要,即使在普通应用中,这种直观的设计也能减少理解和维护成本。

五、总结

基于计数器的循环队列是对传统循环队列的有效改进,它通过增加一个简单的计数器变量,在几乎不增加额外开销的情况下,解决了空间浪费问题,同时简化了队列空满状态的判断逻辑。

这种实现特别适合于:

  • 已知最大容量的场景
  • 对空间利用率有要求的场景
  • 需要直观了解队列元素数量的场景

当然,在实际开发中,我们还可以进一步改进,例如:

  • 使用模板实现通用类型队列
  • 增加动态扩容功能
  • 实现线程安全版本

通过本文的解析,希望读者能理解循环队列的两种实现方式的异同,掌握计数器方式的优势,并能根据实际需求选择合适的队列实现方案。数据结构的选择和实现细节的优化,往往能在实际应用中带来显著的性能提升。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值