Linux C语言实现-------队列

1 什么是队列:

老规矩 , 先上一张数据结构的图(1):


                图1

希望同学们对这张图加以深刻的理解, 对于以后数据结构会有很大的帮助的

图中说明了队列(FIFO)是和 栈(STACK) ,线性表(LIST)(链表,数组),同属于是线性结构,线性结构存储方式分为顺序存储方式和链式存储方式, 上一章的栈已经分别实现了,小伙伴可以看下源码自行体验下,带的有注释的

队列和栈一样 , 也是属于一种受限的线性表, 其特性为跟栈是相反的 , 为先进先出

其只能在其中一端进行插入,叫做队尾(rear), 另一端进行删除,叫做队头(front),在队列中入队n个元素话 , 队列中的元素如下(a1, a2...an),当前a1为队头,an为队尾, 因为先进先出的关系, 出队也只能从a1,a2...依次到an的方式出队,如下图2:


                图2

继续用车来做个比喻吧 ,一个火车通过山洞(队列),从第一节(队头)车厢(数据)先进去,到山洞的另一头,同样也是第一节车厢先出去,依次才到后面的2...n节车厢

2 队列的实现:

因为是线性表, 同样是顺序的方式和链式两种实现

首先是顺序队列:

建立顺序队列,首先要为其静态(数组)或者动态申请(malloc)开辟一块空间,这块空间的大小取决于队列里数据的总大小,队尾(rear)指针始终指向队列的最后一个元素,每次入队操作rear+1, 队头始终指向队列第一次元素,每次出队操作front+1, 因为考虑到内存的合理利用,每次入队或者出队的长度超过队列的总长度的时候,就让它指向队列的第一个元素位置, 即 rear = (rear + 1) % max,front = (front + 1) % max,此时的队列就形成了一个可以循环利用的环形,  我们称之为循环队列。

在循环队列中,判断队列是否为空front == rear , 而是否为满也是front == read,于是乎聪明的先烈们想了个办法 , 放弃队列中的一个节点,用来判断是否队满, 即 (read + 1) % N == front ? 1 : 0。

理一下思路吧

1,队头始终指向出队的元素的位置

2,队尾始终指向入队的元素的位置

1,创建一个空队列, 此时的rear = front = 0;

2,  判断是否空队 , rear == front ? 1 : 0

2, 判断是否满队 , front == (rear + 1) % max ? 1 : 0

3,如果不是满队, 入队 data[rear] = data ; read = (rear + 1) % max;

4,  如果不是空队 , 出队 data = data[front]; front = (front + 1) % max;

代码如下:

//queue.h

#ifndef __FIFO_H__
#define __FIFO_H__

#include <stdio.h>
#include <stdlib.h>

typedef int data_t;
#define N 16 //定义队列最大存储元素数量-1 
typedef struct {
    data_t data[N]; 
    int front;  
    int rear;
}Queue;

Queue *queue_create(void );
int is_queue_empty(Queue *handle);
int is_queue_full(Queue *handle);
int en_queue(Queue *handle, data_t data);
int de_queue(Queue *handle, data_t *data);
int destroy_queue(Queue *handle);
#endif //__FIFO_H__
//queue.c
#include "queue.h"

Queue *queue_create(void) //init 一个空队列
{
    Queue *handle = (Queue *)malloc(sizeof(Queue));
    if (NULL == handle) {
        printf("malloc handle fail ! \n");
        return NULL;
    }

    handle->front = handle->rear = 0; //队首和队尾同时指向队列第一个元素

    return handle;
}

int is_queue_empty(Queue *handle)
{
    if (NULL == handle) {
        return -1;
    }

    return (handle->front == handle->rear ? 1 : 0); //空为1,否则为0
}

int is_queue_full(Queue *handle)
{
    if (NULL == handle) {
        return -1;
    }

    return ((handle->rear + 1) % N == handle->front ? 1 : 0); //满为1,否则为0
}

int en_queue(Queue *handle, data_t data)
{
    if (handle == NULL) {
        return -1; 
    }   
    if (is_queue_full(handle) == 1) { //满对不能入队
        return -1; 
    } 
        
    handle->data[handle->rear] = data; //添加元素到对尾指向的位置
    handle->rear = (handle->rear + 1) % N; //++队尾 % N

    return 0;
}

int de_queue(Queue *handle, data_t *data)
{
    if (NULL == handle) {
        return -1;
    }

    if (is_queue_empty(handle) == 1) { //空队不能出队
        return -1;
    }

    if (data != NULL) {
        *data = handle->data[handle->front]; //传队首元素的值出去
    }
    handle->front = (handle->front + 1) % N; //++队首 % N

    return 0;
}

int destroy_queue(Queue *handle)  //销毁
{
    if (NULL != handle) {
        free(handle);
    }

    return 0;
}

//test.c

#include "queue.h"

int main(void)
{
    int i, num = 0, ret;
    Queue *handle = queue_create(); //1,Init 空队列

    for (i = 0; i < N+4; i++) {//N+4 
        num = i + 1;
        en_queue(handle, num); //2,故意入队超过N(16)的长度
    }   

    while (ret = de_queue(handle, &num) != -1) { //3, 出队,测试入队数据是否正常
        printf("%d ", num); 
    }   
    printf("\n");

    destroy_queue(handle); //4, 销毁队列

    return 0;
}

运行结果:


经测试入队出队正常,超过定长 16-1 个数据即不会入队


//链式队列如图2:


                图2

跟顺序队列一样,队头始终指向第一个入队的元素,队尾始终指向最后入队的元素,只是数据存储的物理地址不是连续开辟的,需要我们用节点的方式,通过next指针链接起来, 代码如下

//queue.h

#ifndef __FIFO_H__
#define __FIFO_H__

#include <stdio.h>
#include <stdlib.h>

typedef int data_t;
#define MAX 16 
typedef struct node_t {
    data_t data;
    struct node_t *next;
}Queue_node, *Queue;

typedef struct list_queue_head{
    Queue rear, front;
}*list_queue_t;
list_queue_t queue_create(void);
int is_queue_empty(list_queue_t handle);
int is_queue_full(list_queue_t handle);
int en_queue(list_queue_t handle, data_t data);
int de_queue(list_queue_t handle, data_t *data);
int destroy_queue(list_queue_t handle);
void queue_clear(list_queue_t handle);
#endif //__FIFO_H__

//queue.c

#include "queue.h"

list_queue_t queue_create(void) //初始化一个空队列
{
    list_queue_t handle = (list_queue_t)malloc(sizeof(struct list_queue_head)); //初始化一个队列的head, 只有rear和front指针,没有数据域
    if (NULL == handle) return NULL;
    handle->rear = handle->front = (Queue )malloc(sizeof(struct node_t)); //给队首和队尾指针申请空间
    if (handle->rear == NULL) return NULL;

    handle->front->next = NULL; //此时队列为空

    return handle;
}

int is_queue_empty(list_queue_t handle) //判断是否空队列
{
    if (NULL == handle) return -1;

    return (NULL == handle->front->next ? 1 : 0); //如果为空返回1,否则返回0
}

int de_queue(list_queue_t handle, data_t *data) //出队
{
    if (NULL == handle || is_queue_empty(handle))   return -1;  //判断是否空队

    Queue remove = handle->front->next; //保存当前队首节点
    handle->front->next = remove->next; //队首节点指向它的下一个节点

    if (NULL != data) {
        *data = remove->data; //当前队首节点的数据传出去
    }

    free(remove); //数据已经传出去了 , free当前队首节点

    return 0;
}

int en_queue(list_queue_t handle, data_t data)//入队
{
    if (NULL == handle || NULL == handle->rear) return -1; 

    Queue node_t = (Queue )malloc(sizeof(struct node_t)); //新节点申请空间
    if (NULL == node_t) return -1;

    node_t->data = data; //新节点赋值
    node_t->next = NULL; //新节点的next指针始终指向NULL

    if (handle->rear == handle->front) { //如果是队列里第一个数据元素节点
        handle->rear = node_t; //队尾指向新节点,下次添加数据队尾的next指针才能正确指向node_t
        handle->front->next = handle->rear; //队首的第一个数据指向当前新节点
    } else {
        handle->rear->next = node_t; //新节点链接到队尾
        handle->rear = node_t; //队尾指向新节点,下次添加数据队尾的next指针才能正确指向node_t
    }

    return 0;
}

void queue_clear(list_queue_t handle)
{
    if (NULL == handle) return;
    Queue remove; //临时保存数据节点

    while (handle->front->next) {
        remove = handle->front->next; //保存队首有数据的节点
        handle->front->next = handle->front->next->next; //当前队首指向下一个节点
        free(remove); //当前队首已经指向下一个节点,free掉当前队首节点
    }

    handle->rear = handle->front; //队尾指针指向数据已经free,重新指向队首
}

int destroy_queue(list_queue_t handle)  //销毁队列
{
    if (NULL != handle) {
        queue_clear(handle);
        free(handle->front); //clear只是free了所有的next指针, 初始化时申请的front指针这里还需要free掉
        free(handle);
    }
    return 0;
}

//test.c

#include "queue.h"

int main(void)
{
    int i, num, ret;
    list_queue_t handle = queue_create(); //1,Init 一个空队列
    for (i = 0; i < MAX; i++) {
        num = i + 1;
        en_queue(handle, num);  //2,入队操作
    }   

    printf("出队: \n");
    while (ret = de_queue(handle, &num) != -1) { //3, 出队,测试入队数据是否正常
        printf("%d ", num); 
    }   
    printf("\n");

    queue_clear(handle); //4, 清空队列, 继续测试出队
    printf("清空之后出队: \n");
    while (ret = de_queue(handle, &num) != -1) { //5, 出队, 测试清空函数是否正确
        printf("%d ", num); 
    }   
    printf("\n");

    destroy_queue(handle); //6 销毁队列
    return 0;
}

运行结果:


猜你喜欢

转载自blog.csdn.net/weixin_38176039/article/details/80295760