c语言循环队列是什么存储结构,【C语言实现】循环队列(内有完整代码)

简书内代码已上传GitHub:点击我 去GitHub查看代码

队列的相关应用已更新:杨辉三角

如有错误,还请大佬们一定留言指出!!

一.什么是队列?

和栈的特性相反,队列是一种 先进先出(FIFO)的线性表。队列只允许在表的一端进行插入,在表的另一端进行删除操作。

在队列中,允许插入操作的一端叫做 队尾(rear),允许删除操作的一端叫做 队头(front 。

如果有一个队列: q = (a1 , a2 , a3 , a4) , 那么a1 是队头元素 , a4是队尾元素。a1也是第一个进入队列的元素,a4是刚刚插入的元素。所以插入的顺序是:a1,a2,a3,a4 , 删除的顺序是:a4,a3,a2,a1.

29a2e257c8a0

队列--图片来源网络

二.怎样实现顺序存储结构的队列?

一开始接触队列,心里就在想:这还不简单?不是和之前顺序栈一样?只是删除操作换了个方向而已...然而,当我开始写了之后...是这样的:

29a2e257c8a0

大吃一惊

??如果直接分配一定的存储空间给队列,然后出队入队...整个队列在一直向前移动...就等于说前面分配的空间就浪费了,且很容易造成内存溢出。

后来脑子又灵光一闪,那我每次出队的时候都把整个队列后移不就好了。。。兴致慢慢的去写了,刚准备开始,又想到:每次删除元素时间复杂度都是o(n)...肯定有更好的解决方法,嗯,就是循环队列。

当队尾移动到队列空间末尾的时候,下一次移动我们移动到队头,相当于把队尾和队头接了起来,这样因为移动而无法利用的空间就充分利用了。

三.队列的结构定义:

需要一个基址保存队列空间地址

需要两个整数front、rear表示索引,访问队头队尾

定义如下:

typedef struct{

// 初始化空间基址

QElemtype *base;

// 队头指针

int front;

// 队尾指针

int rear;

}SqQueue;

接下来就是重点了,也是循环队列必须要会的一些基本操作

29a2e257c8a0

滑稽

四.队列的基本操作:

初始化队列:

分配空间 、 给队头队尾索引赋值

// 初始化队列

Status InitQueue(SqQueue& Q){

Q.base = (QElemtype*)malloc(sizeof(QElemtype) * MAXQSIZE);

if(!Q.base){

printf("error: InitSqQueue fail\n");

return OVERFLOW;

}

// 初始队列队头队尾指向base

Q.front = Q.rear = 0;

return OK;

}

销毁队列:

释放空间 、 给base赋NULL

最好避免二次释放空间

// 销毁队列

Status DestroyQueue(SqQueue& Q){

free(Q.base);

Q.base = NULL;

return OK;

}

清空队列:

队头队尾索引赋0即可

// 清空队列

Status ClearQueue(SqQueue& Q){

Q.front = Q.rear = 0;

return OK;

}

判断队列是否为空:

队头索引 == 队尾索引 即为空

// 判断是否为空

Status QueueEmpty(SqQueue& Q){

return Q.front == Q.rear ? True : False;

}

获得队列长度:

因为是循环队列,所以队尾地址不一定会大于队尾地址

实际长度应该为 队列空间大小 +(rear - front)

// 获取队列长度

int QueueLength(SqQueue Q){

// 循环队列Q.rear - Q.front可能为负

return ((Q.rear - Q.front + MAXQSIZE) % MAXQSIZE);

}

6.获得队头元素:

// 获得队头元素

Status GetHead(SqQueue Q, QElemtype& e){

// 非空则返回队头元素

if(Q.rear != Q.front){

e = Q.base[Q.front];

return OK;

}

printf("error: SqQueue is Empty\n");

return ERROR;

}

队尾插入元素:

插入前判断队列是否已满

插入后更新队尾索引(记住这是个循环队列)

// 队尾插入元素

Status EnQueue(SqQueue& Q, QElemtype e){

//对列满

if((Q.rear + 1) % MAXQSIZE == Q.front)

return ERROR;

// 插入元素

Q.base[Q.rear] = e;

//更新队尾

Q.rear = (Q.rear + 1) % MAXQSIZE;

return OK;

}

删除队头元素:

重点和7点类似

// 删除队头元素

Status DeQueue(SqQueue& Q, QElemtype& e){

//空对列

if(Q.front == Q.rear)

return ERROR;

// 用e返回队头

e = Q.base[Q.front];

// 更新队头

Q.front = (Q.front + 1) % MAXQSIZE;

return OK;

}

9.遍历队列:

// 从队头到队尾visit遍历

Status QueueTraverse(SqQueue Q, void (*visit)(QElemtype)){

//空对列

if(Q.front == Q.rear)

return ERROR;

for(int i = Q.front ;i != Q.rear ; i = (i + 1) % MAXQSIZE){

visit(Q.base[i]);

}

return OK;

}

29a2e257c8a0

都是套路

五.循环队列的应用

再这里不得不又写到约瑟夫问题了:线性表解法

因为循环队列的特性(先进先出),这时候我们只要在计数的同时,删除队头元素,添加至队尾,就可以实现了。

#include "SqQueue.h"

int main(){

int n, m;

scanf("%d %d", &n, &m);

SqQueue q;

InitQueue(q);

//赋值(编号)

for(int i = 1 ; i <= n ; ++i){

EnQueue(q, i);

}

int count = 0, e = 0;

while(QueueLength(q) > 1){

//如果数到m,删除队头元素

if(++count % m == 0){

DeQueue(q, e);

//否则将队头元素删除,添加至队尾

}else{

DeQueue(q, e);

EnQueue(q, e);

}

}

GetHead(q, e);

DestroyQueue(q);

printf("num: %d\n", e);

return 0;

}

另外,有兴趣的话可以看看约瑟夫问题的数学解法。比较n大了这样写还是没有数学解法来的高效:约瑟夫环数学解法

六.完整代码:

#include

#include

//最大队列长度

#define MAXQSIZE 100

//定义状态

#define OVERFLOW -1

#define OK 1

#define ERROR 0

#define True 1

#define False 0

typedef int QElemtype;

typedef int Status;

typedef struct{

// 初始化空间基址

QElemtype *base;

// 队头指针

int front;

// 队尾指针

int rear;

}SqQueue;

// 初始化队列

Status InitQueue(SqQueue& Q){

Q.base = (QElemtype*)malloc(sizeof(QElemtype) * MAXQSIZE);

if(!Q.base){

printf("error: InitSqQueue fail\n");

return OVERFLOW;

}

// 初始队列队头队尾指向base

Q.front = Q.rear = 0;

return OK;

}

// 销毁队列

Status DestroyQueue(SqQueue& Q){

free(Q.base);

Q.base = NULL;

return OK;

}

// 清空队列

Status ClearQueue(SqQueue& Q){

Q.front = Q.rear = 0;

return OK;

}

// 判断是否为空

Status QueueEmpty(SqQueue& Q){

return Q.front == Q.rear ? True : False;

}

// 获取队列长度

int QueueLength(SqQueue Q){

// 循环队列Q.rear - Q.front可能为负

return ((Q.rear - Q.front + MAXQSIZE) % MAXQSIZE);

}

// 获得队头元素

Status GetHead(SqQueue Q, QElemtype& e){

// 非空则返回队头元素

if(Q.rear != Q.front){

e = Q.base[Q.front];

return OK;

}

printf("error: SqQueue is Empty\n");

return ERROR;

}

// 队尾插入元素

Status EnQueue(SqQueue& Q, QElemtype e){

//对列满

if((Q.rear + 1) % MAXQSIZE == Q.front)

return ERROR;

// 插入元素

Q.base[Q.rear] = e;

//更新队尾

Q.rear = (Q.rear + 1) % MAXQSIZE;

return OK;

}

// 删除队头元素

Status DeQueue(SqQueue& Q, QElemtype& e){

//空对列

if(Q.front == Q.rear)

return ERROR;

// 用e返回队头

e = Q.base[Q.front];

// 更新队头

Q.front = (Q.front + 1) % MAXQSIZE;

return OK;

}

// 从队头到队尾visit遍历

Status QueueTraverse(SqQueue Q, void (*visit)(QElemtype)){

//空对列

if(Q.front == Q.rear)

return ERROR;

for(int i = Q.front ;i != Q.rear ; i = (i + 1) % MAXQSIZE){

visit(Q.base[i]);

}

return OK;

}

每天进步一点,加油!

29a2e257c8a0

End

END

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值