3.3.1【顺序循环队列】

1. 背景引入

为了避免当只有一个元素时,队头和队尾重合使得处理变得麻烦,所以引入两个指针frontrear

front即 队头指针 指向队头元素rear即队尾指针 指向 队尾元素的下一个元素。

这会导致牺牲一个空的内存位置,来解决“假溢出”问题(数组越界问题)。

2. 概念

解决假溢出的办法就是:后面满了,再从头开始,也就是头尾相接的循环

我们把队列的这种 头尾相接的 顺序存储结构 称为循环队列

2.1. 特点

  1. 逻辑结构:线性结构
  2. 物理结构:顺序结构

2.2. 注意的问题

  • 循环队列,要特别注意 入队出队后,队头队尾变量的更新方式
    • 注意理解这两种更新办法的 原因
  • 队列长度计算,当做公式理解,利用取余特性

3. 接口实现

3.1. 定义 操作 顺序循环队列的 结构体

队列 或者 栈,都是对 顺序表 进行受限操作来实现的。

所以,结构体顺带开辟 数组作为顺序表空间。

3.2. 创建空的 顺序循环队列(头尾 相等)

不管 队头队尾指针下标为多少,只要相等,就是空的 队列

3.3. 入队(栈尾后移)

注意,队头、队尾,下标更新的方式。

入队,从队尾进入,即队尾指针后移。

但要注意,入队后,队尾指针后移,其下标所在的空间是不允许被使用的。

所以,N个内存宽度的数组,只有N-1可以真的使用,剩下一个浪费掉,作为判断用。

3.4. 出队(队头后移)

注意,队头、队尾,下标更新的方式。

队头指针后移即可

3.5. 队列 长度(即当前被用上的 元素个数)

最长的长度,就是N-1,总有个空间不允许被使用

3.6. 清空(头尾 相等,即不停的出队,直到队空)

队头队尾,指向相同即可。

4. 总体代码

#include <stdio.h>
#include <stdlib.h>
#define DEBUG(Str) printf("%s %s %s %d\n", Str, __func__, __FILE__, __LINE__)

// 定义 操作 顺序循环队列的 结构体
#define N 5 //队列里的顺序表长度
typedef int SQDataType;
typedef struct SeqQueue
{
    SQDataType data[N]; //直接开辟定长数组
    int front;          //队头 下标
    int rear;           //队尾 下标
} SQ;

// 创建空的 顺序循环队列(头尾 相等)
// 形参为空,一定写成void,避免用时意外传值,段错误难排查
SQ *SQInit(void)
{
    // 开辟 操作 顺序循环队列的 结构体
    SQ *PQ = (SQ *)malloc(sizeof(SQ));
    if (NULL == PQ)
    {
        DEBUG("SQInit err");
        return NULL;
    }

    // 初始化 队头 队尾 下标,为相同值即可
    PQ->front = PQ->rear = 0;

    return PQ;
}

// 入队(栈尾后移)
int SQPush(SQ *PQ, SQDataType data)
{
    // 判断 满不满,满了不能入队
    // 因为从队尾进,若满,队尾 下个位置就是 队头
    if ((PQ->rear + 1) % N == PQ->front)
    {
        printf("now wanna push data:%d\n", data);
        DEBUG("SQPush failed, FULL");
        return -1;
    }

    // 数据入列
    // 把上回不可用的队尾位置,变为可用;
    // 队尾挪到下个位置,继续不可用。
    // 循环队列就算满了,必会有一个rear位置,不可被使用
    PQ->data[PQ->rear] = data;

    // 必须这种方法 赋值更新位置,因为要循环
    PQ->rear = (PQ->rear + 1) % N;

    return 0;
}

// 出队(队头后移)
SQDataType SQPop(SQ *PQ)
{
    // 空则不能出队
    if (PQ->rear == PQ->front)
    {
        DEBUG("SQPop failed, SQ is empty");
        return (SQDataType)(-1);
    }

    // 临时变量接收 要出队的数据
    SQDataType data = PQ->data[PQ->front];

    // 出队是 队头 后移
    PQ->front = (PQ->front + 1) % N;

    // 返回数据
    return data;
}

// 队列 长度(即当前被用上的 元素个数)
int SQLength(SQ *PQ)
{
    // 队尾先加,因为了逻辑上 队尾编号在后
    // 加N 为保证 队尾减对头 不出负数
    // 就算为正,用%N 也可以去除 商,只保留 余数
    return (PQ->rear + N - PQ->front) % N;
}

// 清空(头尾 相等,即不停的出队,直到队空)
void SQClear(SQ *PQ)
{
#if 0 //都行
    while (PQ->front != PQ->rear)
        SQPop(PQ);

#elif 1
    // 同值即可
    PQ->front = PQ->rear;

#endif
}

int main(int argc, char *argv[])
{
    // 创建一个空的队列
    SQ *PQ = SQInit();

    // 入列 0 1 2 3 4
    // 注意:N为5个内存长度,实际只有N-1 4个可以用
    for (int i = 1; i <= N; i++)
        SQPush(PQ, i * 11);

    // 出队
    // 队头出队,队头指针 后移
    printf("front:%d\n", SQPop(PQ));

    // 有效 队列元素个数:长度
    // 22 33 44
    printf("SeqQueu Length:%d\n", SQLength(PQ));

    // 清空队列
    SQClear(PQ);
    printf("-----SeqQueu Length:%d\n", SQLength(PQ));

    // 释放循环队列
    free(PQ);
    PQ = NULL;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值