C 数据结构 之 队列

队列

1. 使用方法

  • 每次只能加入一个数字,每次只能拿出一个数字
  • 队列里的数字有前后顺序,先进的数字在前,后进的数字在后
  • 每次从队列里获得的数字一定是最先放进去的数字(先进先出)

2. 实现方法

typedef struct {
	int buf[size]; //存放数字的数组
	int head;      //第一个数字所在的下标(如果队列空则 head 应该等于 tail )
	int tail;      //下一个数字应该在的下标
}queue;
  • 除了这个结构体,还需要为这个队列编写一组相关的关联函数
    • 队列的初始化函数 用来把一个没有使用过的队列设置成可使用的状态
      • void queue_init(queue *p_queue) {…}
    • 队列的清理函数 用来把一个使用过的队列里的所有数字删除
      • void queue_deinit(queue*p_queue){…}
    • 获得队列里的数字个数的函数
      • int queue_size(const queue *p_queue){…}
    • 判断队列是否是空的函数
      • int queue_empty(const queue *p_queue){…}
    • 判断队列是否满的函数
      • int queue_full(const queue *p_queue){…}
    • 向队列里加数字的函数(用整数类型返回值表示是否把数字加进了队列里)
      • int queue_push(queue *p_queue, int val){…}
    • 从队列里获得数字的函数(同时删除数字)
      • int queue_pop(queue *p_queue, int *p_val){…}
    • 从队列里获得数字的函数(不会删除数字)
      • int queue_top(const queue *p_queue, int *p_val){…}
/
//queue_1.h
#ifndef  __QUEUE_1_H__
#define  __QUEUE_1_H__

typedef struct {
    int buf[SIZE];  //记录队列里的数字
    int head;       //记录第一个数字所在存储区的下标
    int tail;       //记录下一个数字应该放置的下标
}queue;

//队列初始化函数
void queue_init(queue * p_queue);

//队列的清理函数
void queue_deinit(queue *p_queue);

//获得队列里数字个数的函数
int queue_size(const queue *p_queue);

//判断队列是否为空的函数
int queue_empty(const queue *p_queue);

//判断队列是否满的函数
int queue_full(const queue *p_queue);

//向队列里加入数字的函数 
int queue_push(queue *p_queue, int val);

//从队列获得数字的函数(同时删除数字)
int queue_pop(queue *p_queue, int *p_val);

//从队列获得数字的函数(同时删除数字)
int queue_front(const queue *p_queue, int *p_val);

#endif  //QUEUE_1_H__

/
//queue_1.c
#include "queue_1.h"

//队列初始化函数
void queue_init(queue * p_queue) {
    p_queue->tail = 0;  //在队列没有数字的时候,下一个数字应该放在下标为0的存储区里
    p_queue->head = 0;  //head 等于 tail表示队列里没有数字
}

//队列的清理函数
void queue_deinit(queue *p_queue) {
    p_queue->tail = 0;  //在队列没有数字的时候,下一个数字应该放在下标为0的存储区里
    p_queue->head = 0;  //head 等于 tail表示队列里没有数字
}

//获得队列里数字个数的函数
int queue_size(const queue *p_queue) {
    //当tail和head相等时返回0
    //每当队里里多一个数,tail 会加1,相减结果就会加1
    //每当队列里少一个数,head 会加1,相减结果就会减1 
    return p_queue->tail - p_queue->head;
}

//判断队列是否为空的函数
int queue_empty(const queue *p_queue) {
    return p_queue->head == p_queue->tail;
}

//判断队列是否满的函数
int queue_full(const queue *p_queue) {
    //队列也有两种实现方法,一种是从前向后使用这些存储区
    //换句话说,只要队列最后一个存储区被填上了数字,那么就可以认为这个队列已经满了
    //虽然队列先进先出的定义下,当最后一个存储区被填上数字之后,前面下标为为0的存储区
    //的数可能已经被拿走了,这个时候下标为0的存储区是可以继续用来填数字的
    //但现在实现的这个版本,可以认为,当最后一个存储区被填上数字就可以认为这个存储区已经满了 
    return p_queue->tail >= SIZE;
}

//向队列里加入数字的函数 
int queue_push(queue *p_queue, int val) {
    if (p_queue->tail >= SIZE) {
        //队列已经满了
        return 0; 
    }
    p_queue->buf[p_queue->tail] = val; //把新数组记录到数组里以 tail 做下标的存储区里
    p_queue->tail++;                   //把tail向后移动一步
    return 1;
}

//从队列获得数字的函数(同时删除数字)
int queue_pop(queue *p_queue, int *p_val) {
    if (p_queue->tail == p_queue->head) {
        //如果队列是空的
        return 0;
    }
    *p_val = p_queue->buf[p_queue->head]; //把数组里以head做下标的的存储区的数字传递给调用函数
    p_queue->head++;                      //把head向后移动一步,表示删除这个数字
    return 1;
}

//从队列获得数字的函数(不删除数字)
int queue_front(const queue *p_queue, int *p_val) {
    if (p_queue->tail == p_queue->head) {
        return 0;
    }
    *p_val = p_queue->buf[p_queue->head];
    return 1;
}

/
//queue_main.c
#include <stdio.h>
//如果一个源文件里使用了某个头文件里声明的函数,那这个头文件也是这个源文件的必要头文件
#include "queue_1.h"
int main() {
    //声明一个结构体变量代表一个队列,然后对队列做初始化,最后做清理
    //使用数据结构的基本代码框架
    queue que = {0};
    int val = 0;

    queue_init(&que);
    printf("数字个数是%d\n", queue_size(&que));
    printf("队列是否空%d\n", queue_empty(&que));
    printf("队列是否满%d\n", queue_full(&que));

    queue_push(&que, 10);
    queue_push(&que, 20);
    queue_push(&que, 30);
    printf("数字个数是%d\n", queue_size(&que));
    printf("队列是否空%d\n", queue_empty(&que));
    printf("队列是否满%d\n", queue_full(&que));

    queue_front(&que, &val);
    printf("最前面的数字是%d\n", val);

    queue_pop(&que, &val);
    printf("最前面的数字是%d\n", val);
    queue_pop(&que, &val);
    printf("最前面的数字是%d\n", val);

    //这个时候第一个和第二个存储区数字已经被删了
    //但是当 SIZE 下标存储区被填满()过(tail就等于了SIZE),(这个版本)依然认为队列满了
    //所以最后即使队列里的数字被删完了,依然显示队列是满的
    queue_push(&que, 40);
    queue_push(&que, 50);
    printf("数字个数是%d\n", queue_size(&que));
    printf("队列是否空%d\n", queue_empty(&que));
    printf("队列是否满%d\n", queue_full(&que));

    while(1) {
        if(!queue_pop(&que, &val))
        {
            break;
        }
        printf("%d\n", val);
    }

    printf("数字个数是%d\n", queue_size(&que));
    printf("队列是否空%d\n", queue_empty(&que));
    printf("队列是否满%d\n", queue_full(&que));
    
    queue_deinit(&que);

    return 0;
}

/*
gcc -DSIZE=5 queue_1.c queue_main.c
output:
数字个数是0
队列是否空1
队列是否满0
数字个数是3
队列是否空0
队列是否满0
最前面的数字是10
最前面的数字是10
最前面的数字是20
数字个数是3
队列是否空0
队列是否满1
30
40
50
数字个数是0
队列是否空1
队列是否满1
*/
  • 回想一下刚刚讲过的队列,是从前向后使用存储区,当最后一个存储区使用完以后,就不能在往里加入数字了。如果能重复利用已经被拿走数字的存储区,40 和 50 其实是可以放进去的。所以就有了循环队列

循环队列

当数组里最后一个存储区也被放上数字以后可以把后边的数字放在 下标为0的存储区里(这个存储区里必须没有有效数字)

1. 实现方法

typedef struct{
int buf[SIZE]; //记录数字的数组
int qty; //队列里的数字个数
int tail; //下一个数字所在的下标
} queue;

loop_queue_1.h

// 前后加上预处理指令
#ifndef     __LOOP_QUEUE_1_H__
#define     __LOOP_QUEUE_1_H__


// qty 可能大于也可能小于 tail
// 当tail 因为大于等于 size 被置为0时
typedef struct{
    int buf[SIZE]; //记录数字的数组
    int qty; //队列里的实际数字个数
    int tail; //下一个数字所在的下标
} queue;

// 删除函数内容和形参名称

// get_head 函数只是在队列里面使用
// 我们不希望队列以外的函数来使用get_head 函数
// 所以 get_head 函数的生命不应该出现在头文件里
// 获得最前面数字所在下标的函数
// int get_head(const queue *);

// 队列的初始化函数
void queue_init(queue *);

// 队列清理函数
void queue_deinit(queue *);

//获得队列里数字个数的函数
int queue_size(const queue *);

// 判断队列是否空的函数
int queue_empty(const queue *);

// 判断队列是否满的函数
int queue_full(const queue *);

// 向队列里加入数字的函数
int queue_push(queue *, int);

// 从队列里获得数字的函数(同时删除数字)
int queue_pop(queue *, int *);

// 从队列里获得数字(不会删除数字)
int queue_front(const queue *, int *);

#endif      __LOOP_QUEUE_1_H__

loop_queue_1.c

#include "loop_queue_1.h"

// 获得最前面数字所在下标的函数
int get_head(const queue *p_queue) {
    int ret = p_queue->tail - p_queue->qty;
    // 当tail被置0以后(由于queue_pop函数,tail比qty先到SIZE), 就会出现ret<0的情况
    // 并且这种情况出现以后,ret<0 将一直保持下去,因为每次queue_push 都会给 tail和qty都加一
    if (ret < 0) {
        ret += SIZE;
    }
    return ret;
}

// 队列的初始化函数
void queue_init(queue *p_queue) {
    p_queue->tail = 0;
    p_queue->qty = 0; //代表队列里没有数字
}

// 队列清理函数
void queue_deinit(queue *p_queue) {
    p_queue->tail = 0;
    p_queue->qty = 0;
}

// 获得队列里数字个数的函数
int queue_size(const queue *p_queue){
    // qty 成员变量当做返回值传递给调用函数
    return p_queue->qty;
}

// 判断队列是否空的函数
int queue_empty(const queue *p_queue) {
    return !p_queue->qty;
}

// 判断队列是否满的函数
int queue_full(const queue *p_queue) {
    return p_queue->qty >= SIZE;
}

// 向队列里加入数字的函数
int queue_push(queue *p_queue, int val) {
    if (p_queue->qty >= SIZE){
        //队列满了
        return 0;
    }
    p_queue->buf[p_queue->tail] = val;
    p_queue->tail++;
    p_queue->qty++;
    // tail 一直加可能会超过存储区范围(当qty因为queue_pop没有超过SIZE,但tail超过SIZE后)
    // 当tail = size ,把tail 置为0,就可以绕着圈的填数字了
    if (p_queue->tail >= SIZE) {
        p_queue->tail = 0;
    }

    // 这两每次一起加,对get_head的结果不会有任何改变
    return 1;
}

// 从队列里获得数字的函数(同时删除数字)
int queue_pop(queue *p_queue, int *p_val) {
    if (!p_queue->qty) {
        // 队列是空的
        return 0;
    }
    // 使用get_head 函数得到第一个数字所在的下标,把这个存储区里的数字传递给调用函数
    *p_val = p_queue->buf[get_head(p_queue)]; 
    // 把qty 减一 就相当于删除了这个数字
    p_queue->qty--;
    // 然后 get_head 的结果就会加一
    return 1;
}

// 从队列里获得数字(不会删除数字)
int queue_front(const queue *p_queue, int *p_val) {
    if (!p_queue->qty) {
        return 0;
    }
    *p_val = p_queue->buf[get_head(p_queue)];
}

loop_queue_main.c

#include <stdio.h>
//如果一个源文件里使用了某个头文件里声明的函数,那这个头文件也是这个源文件的必要头文件
#include "loop_queue_1.h"
int main() {
    //声明一个结构体变量代表一个队列,然后对队列做初始化,最后做清理
    //使用数据结构的基本代码框架
    queue que = {0};
    int val = 0;

    queue_init(&que);
    printf("数字个数是%d\n", queue_size(&que));
    printf("队列是否空%d\n", queue_empty(&que));
    printf("队列是否满%d\n", queue_full(&que));

    queue_push(&que, 10);
    queue_push(&que, 20);
    queue_push(&que, 30);
    printf("数字个数是%d\n", queue_size(&que));
    printf("队列是否空%d\n", queue_empty(&que));
    printf("队列是否满%d\n", queue_full(&que));

    queue_front(&que, &val);
    printf("最前面的数字是%d\n", val);

    queue_pop(&que, &val);
    printf("最前面的数字是%d\n", val);
    queue_pop(&que, &val);
    printf("最前面的数字是%d\n", val);


    queue_push(&que, 40);
    queue_push(&que, 50);
    printf("数字个数是%d\n", queue_size(&que));
    printf("队列是否空%d\n", queue_empty(&que));
    // 上一个版本这里的判满结果是真(size = 5),因为已经把最后存储区用了
    // 这个结果判满的结果是假,因为前面那个 10和20 的存储区已经空出来了
    printf("队列是否满%d\n", queue_full(&que));

    while(1) {
        if(!queue_pop(&que, &val))
        {
            break;
        }
        printf("%d\n", val);
    }

    printf("数字个数是%d\n", queue_size(&que));
    printf("队列是否空%d\n", queue_empty(&que));
    printf("队列是否满%d\n", queue_full(&que));
    
    queue_deinit(&que);

    return 0;
}

/*
gcc -DSIZE=5 loop_queue_1.c loop_queue_main.c
output:
数字个数是0
队列是否空1
队列是否满0
数字个数是3
队列是否空0
队列是否满0
最前面的数字是10
最前面的数字是10
最前面的数字是20
数字个数是3
队列是否空0
队列是否满0 ****这里和之前有不同,上个版本这里是1****
30
40
50
数字个数是0
队列是否空1
队列是否满0
*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值