day19——三、队列

3.1 队列介绍

1> 队列也是操作受限的线性表:所有操作只能在端点处进行,其删除和插入必须在不同端进行

2> 允许插入操作的一端称为队尾,允许删除操作的一端称为队头

3> 特点:先进先出(FIFO)

4> 分类:

顺序存储的队列称为顺序队列

链式存储的队列,称为链式队列

3.2 顺序队列

1> 使用一片连续存储的空间存储队列,并且给定两个变量,分别记录队头和队尾下标

2> 普通顺序队列使用中,存在“假溢满”现象

假溢满:队列中明明还有存储空间,但是由于队尾已经达到了数组的最大下标,不能在继续入队元素了

3> 为了解决“假溢满”现象,我们引入了循环顺序队列

3.3 循环顺序队列

1> 循环顺序队列:

通过相关操作,当对头或队尾达到数组最大下标时,可以返回到下标为0的位置

2> 结构体类型:

一个数组存储队列,两个变量分别存储队头和队尾的下标

注意:需要人为浪费一个存储空间,用于判满

#define MAX 8          //队列最大长度
typedef int datatype;   //数据元素类型
//定义队列类型
typedef struct
{
    datatype data[MAX];   //存储队列的容器
    int front;          //记录队头下标
    int tail;           //记录队尾下标
}SeqQueue, *SeqQueuePtr;

3> 创建队列

//创建队列
SeqQueuePtr queue_create()
{
    //在堆区申请一个队列的大小
    SeqQueuePtr Q =(SeqQueuePtr)malloc(sizeof(SeqQueue));
    if(NULL==Q)
    {
        printf("创建失败\n");
        return NULL;
    }

    //初始化
    bzero(Q->data, sizeof(Q->data));  //初始化数组
    Q->front = Q->tail = 0;          //队列为空

    printf("队列创建成功\n");
    return Q;
}

4> 判空判满

//队列判空
int queue_empty(SeqQueuePtr Q)
{
    return Q->front == Q->tail;
}

//队列判满
int queue_full(SeqQueuePtr Q)
{
    return (Q->tail+1)%MAX == Q->front;
}

5> 入队:将数据放入队尾所在位置

//入队
void queue_push(SeqQueuePtr Q, datatype e)
{
    //判断逻辑
    if(NULL==Q || queue_full(Q))
    {
        printf("入队失败\n");
        return ;
    }

    //入队逻辑
    Q->data[Q->tail] = e;

    //队列变化:队尾后移
    Q->tail = (Q->tail+1)%MAX;

    printf("入队成功\n");
}

6> 遍历队

//遍历队列
void queue_show(SeqQueuePtr Q)
{
    //判断逻辑
    if(NULL==Q || queue_empty(Q))
    {
        printf("遍历失败\n");
        return ;
    }

    //遍历逻辑
    printf("从队头到队尾元素分别是:");
    for(int i=Q->front; i!=Q->tail; i=(i+1)%MAX)
    {
        printf("%d\t", Q->data[i]);
    }
    printf("\n");
}

7> 出队

//出队
void queue_pop(SeqQueuePtr Q)
{
    //判断逻辑
    if(NULL==Q || queue_empty(Q))
    {
        printf("出队失败\n");
        return;
    }

    //出队逻辑
    printf("%d出队成功\n", Q->data[Q->front]);

    //队头后移
    Q->front = (Q->front+1)%MAX;

}

8> 求队列的大小:只用队头和队尾完成

//求队列的大小
int queue_size(SeqQueuePtr Q)
{
    //在不使用循环的情况下求大小
    //判断逻辑
    if(NULL == Q)
    {
        printf("失败\n");
        return -1;
    }

    return (Q->tail-Q->front+MAX)%MAX;    //核心语句
}

9> 销毁队列

//销毁队列
void queue_destroy(SeqQueuePtr Q)
{
    if(NULL != Q)
    {
        free(Q);
        Q = NULL;
    }

    printf("销毁成功\n");
}

3.4 所有代码

1.c

#include"1.h"

int main(int argc, const char *argv[])
{
    //调用创建队列函数
    SeqQueuePtr Q = queue_create();
    if(NULL == Q)
    {
        return -1;
    }
    
    //调用入队函数
    queue_push(Q, 520);
    queue_push(Q, 1314);
    queue_push(Q, 666);
    queue_push(Q, 999);

    //调用遍历函数
    queue_show(Q);

    //调用出队函数
    queue_pop(Q);
    queue_pop(Q);
    queue_pop(Q);
    queue_pop(Q);
    queue_pop(Q);

    queue_show(Q);

    //销毁队列
    queue_destroy(Q);
    Q = NULL;




    
    return 0;
}

2.c

#include"1.h"

//创建队列
SeqQueuePtr queue_create()
{
    //在堆区申请一个队列的大小
    SeqQueuePtr Q =(SeqQueuePtr)malloc(sizeof(SeqQueue));
    if(NULL==Q)
    {
        printf("创建失败\n");
        return NULL;
    }

    //初始化
    bzero(Q->data, sizeof(Q->data));  //初始化数组
    Q->front = Q->tail = 0;          //队列为空

    printf("队列创建成功\n");
    return Q;
}

//队列判空
int queue_empty(SeqQueuePtr Q)
{
    return Q->front == Q->tail;
}

//队列判满
int queue_full(SeqQueuePtr Q)
{
    return (Q->tail+1)%MAX == Q->front;
}

//入队
void queue_push(SeqQueuePtr Q, datatype e)
{
    //判断逻辑
    if(NULL==Q || queue_full(Q))
    {
        printf("入队失败\n");
        return ;
    }

    //入队逻辑
    Q->data[Q->tail] = e;

    //队列变化:队尾后移
    Q->tail = (Q->tail+1)%MAX;

    printf("入队成功\n");
}

//遍历队列
void queue_show(SeqQueuePtr Q)
{
    //判断逻辑
    if(NULL==Q || queue_empty(Q))
    {
        printf("遍历失败\n");
        return ;
    }

    //遍历逻辑
    printf("从队头到队尾元素分别是:");
    for(int i=Q->front; i!=Q->tail; i=(i+1)%MAX)
    {
        printf("%d\t", Q->data[i]);
    }
    printf("\n");
}

//出队
void queue_pop(SeqQueuePtr Q)
{
    //判断逻辑
    if(NULL==Q || queue_empty(Q))
    {
        printf("出队失败\n");
        return;
    }

    //出队逻辑
    printf("%d出队成功\n", Q->data[Q->front]);

    //队头后移
    Q->front = (Q->front+1)%MAX;

}

//求队列的大小
int queue_size(SeqQueuePtr Q)
{
    //在不使用循环的情况下求大小
    //判断逻辑
    if(NULL == Q)
    {
        printf("失败\n");
        return -1;
    }

    return (Q->tail-Q->front+MAX)%MAX;    //核心语句
}

//销毁队列
void queue_destroy(SeqQueuePtr Q)
{
    if(NULL != Q)
    {
        free(Q);
        Q = NULL;
    }

    printf("销毁成功\n");
}

1.h

#ifndef LOOPSEQQUEUE_H
#define LOOPSEQQUEUE_H

#include<myhead.h>

#define MAX 8          //队列最大长度
typedef int datatype;   //数据元素类型
//定义队列类型
typedef struct
{
    datatype data[MAX];   //存储队列的容器
    int front;          //记录队头下标
    int tail;           //记录队尾下标
}SeqQueue, *SeqQueuePtr;

//创建队列
SeqQueuePtr queue_create();

//队列判空
int queue_empty(SeqQueuePtr Q);

//队列判满
int queue_full(SeqQueuePtr Q);

//入队
void queue_push(SeqQueuePtr Q, datatype e);

//遍历队列
void queue_show(SeqQueuePtr Q);

//出队
void queue_pop(SeqQueuePtr Q);

//求队列的大小
int queue_size(SeqQueuePtr Q);

//销毁队列
void queue_destroy(SeqQueuePtr Q);

#endif

3.5 链式队列

1> 链式存储的队列称为链式队列

2> 实现原理:

单向链表头插尾删实现:链表的头部就是队尾,链表的尾部就是队头

单向链表头删尾插实现:链表的头部就是队头,链表的尾部就是队尾

但是:上述操作中,都要用到链表尾部节点,都需要遍历整个链表完成,效率较低

此时,我们可以引入尾指针的概念,专门指向最后一个节点的指针。

3> 将一个头指针和一个尾指针封装成一个队列

4> 队列类型

//定义数据元素类型
typedef int datatype;

//定义结点类型
typedef struct Node
{
    union
    {
        datatype data;  //数据域
        int len;      //长度
    };

    struct Node *next;   //指针域
}Node, *NodePtr;

//定义队列类型
typedef struct 
{
    NodePtr head;    //头指针
    NodePtr tail;    //尾指针
}Queue, *QueuePtr;

5> 创建队列

注意:先创建出队列、然后创建链表、将队列的两个指针指向链表

//创建队列
QueuePtr queue_create()
{
    //堆区申请一个队列的空间
    QueuePtr Q = (QueuePtr)malloc(sizeof(Queue));
    if(NULL == Q)
    {
        printf("创建失败\n");
        return NULL;
    }

    //此时 Q->head 和Q->tail是两个野指针

    //创建一个链表
    Q->head = (NodePtr)malloc(sizeof(Node));
    if(Q->head == NULL)
    {
        printf("创建失败\n");
        free(Q);     //释放队列空间
        return NULL;
    }
    //给头结点初始化
    Q->head->len = 0;
    Q->head->next = NULL;
    
    //将两个指针指向头结点
    Q->tail = Q->head;

    printf("创建成功\n");
    return Q;

}

6> 队列判空

只需要队头和队尾都指向头结点即可

//判空
int queue_empty(QueuePtr Q)
{
    return Q->head == Q->tail;
}

7> 入队

//入队
void queue_push(QueuePtr Q, datatype e)
{
    //判断逻辑
    if(NULL==Q)
    {
        printf("入队失败\n");
        return;
    }

    //申请结点封装数据
    NodePtr p = (NodePtr)malloc(sizeof(Node));
    if(NULL==p)
    {
        printf("入队失败\n");
        return;
    }
    //初始化
    p->data = e;
    p->next = NULL;

    //尾插
    Q->tail->next = p;    //将结点连接到链表上

    //更新尾指针
    Q->tail = p;

    //表长变化
    Q->head->len++;

    printf("入队成功\n");
}

8> 遍历队

//遍历队列
void queue_show(QueuePtr Q)
{
    //判断逻辑
    if(NULL==Q || queue_empty(Q))
    {
        printf("遍历失败\n");
        return ;
    }

    //定义遍历指针,从第一个结点出发
    NodePtr q = Q->head->next;
    while(q!=NULL)
    {
        //只要结点不为空,就输出数据域
        printf("%d\t", q->data);

        q = q->next;     //向后遍历
    }
    printf("\n");

}

9> 出队

出队时,需要注意,如果所有节点全部出队,则需要将尾指针重新指向头结点

//出队
void queue_pop(QueuePtr Q)
{
    //判断逻辑
    if(NULL==Q || queue_empty(Q))
    {
        printf("出队失败\n");
        return ;
    }

    //出队:头删
    NodePtr p = Q->head->next;    //标记
    Q->head->next = p->next;      //孤立

    printf("%d出队成功\n", p->data);
    free(p);                      //删除
    p = NULL;

    //判断是否已经全部删除
    if(Q->head->next == NULL)
    {
        //尾指针重新指向头结点
        Q->tail = Q->head;
    }


    //表长变化
    Q->head->len--;

}

10> 销毁队伍

//销毁队列
void queue_destroy(QueuePtr Q)
{
    //判断逻辑
    if(NULL==Q)
    {
        return;
    }

    //释放所有的结点
    while(!queue_empty(Q))
    {
        queue_pop(Q);      //不断将结点出队
    }
    
    //释放头结点
    free(Q->head);
    Q->head=Q->tail = NULL;    //防止野指针

    //释放队列空间
    free(Q);
    Q = NULL;
    printf("释放成功\n");
}

11> 求队列长度

//求队列长度
int queue_size(QueuePtr Q)
{
    //判断逻辑
    if(NULL==Q)
    {
        return -1;
    }

    return Q->head->len;

}

3.6 所有代码

1.c

#include"1.h"

//创建队列
QueuePtr queue_create()
{
    //堆区申请一个队列的空间
    QueuePtr Q = (QueuePtr)malloc(sizeof(Queue));
    if(NULL == Q)
    {
        printf("创建失败\n");
        return NULL;
    }

    //此时 Q->head 和Q->tail是两个野指针

    //创建一个链表
    Q->head = (NodePtr)malloc(sizeof(Node));
    if(Q->head == NULL)
    {
        printf("创建失败\n");
        free(Q);     //释放队列空间
        return NULL;
    }
    //给头结点初始化
    Q->head->len = 0;
    Q->head->next = NULL;
    
    //将两个指针指向头结点
    Q->tail = Q->head;

    printf("创建成功\n");
    return Q;

}

//判空
int queue_empty(QueuePtr Q)
{
    return Q->head == Q->tail;
}

//入队
void queue_push(QueuePtr Q, datatype e)
{
    //判断逻辑
    if(NULL==Q)
    {
        printf("入队失败\n");
        return;
    }

    //申请结点封装数据
    NodePtr p = (NodePtr)malloc(sizeof(Node));
    if(NULL==p)
    {
        printf("入队失败\n");
        return;
    }
    //初始化
    p->data = e;
    p->next = NULL;

    //尾插
    Q->tail->next = p;    //将结点连接到链表上

    //更新尾指针
    Q->tail = p;

    //表长变化
    Q->head->len++;

    printf("入队成功\n");
}

//遍历队列
void queue_show(QueuePtr Q)
{
    //判断逻辑
    if(NULL==Q || queue_empty(Q))
    {
        printf("遍历失败\n");
        return ;
    }

    //定义遍历指针,从第一个结点出发
    NodePtr q = Q->head->next;
    while(q!=NULL)
    {
        //只要结点不为空,就输出数据域
        printf("%d\t", q->data);

        q = q->next;     //向后遍历
    }
    printf("\n");

}

//出队
void queue_pop(QueuePtr Q)
{
    //判断逻辑
    if(NULL==Q || queue_empty(Q))
    {
        printf("出队失败\n");
        return ;
    }

    //出队:头删
    NodePtr p = Q->head->next;    //标记
    Q->head->next = p->next;      //孤立

    printf("%d出队成功\n", p->data);
    free(p);                      //删除
    p = NULL;

    //判断是否已经全部删除
    if(Q->head->next == NULL)
    {
        //尾指针重新指向头结点
        Q->tail = Q->head;
    }


    //表长变化
    Q->head->len--;

}

//求队列长度
int queue_size(QueuePtr Q)
{
    //判断逻辑
    if(NULL==Q)
    {
        return -1;
    }

    return Q->head->len;

}

//销毁队列
void queue_destroy(QueuePtr Q)
{
    //判断逻辑
    if(NULL==Q)
    {
        return;
    }

    //释放所有的结点
    while(!queue_empty(Q))
    {
        queue_pop(Q);      //不断将结点出队
    }
    
    //释放头结点
    free(Q->head);
    Q->head=Q->tail = NULL;    //防止野指针

    //释放队列空间
    free(Q);
    Q = NULL;
    printf("释放成功\n");
}

2.c

#include"1.h"

int main(int argc, const char *argv[])
{
    //调用创建函数
    QueuePtr Q = queue_create();
    if(NULL==Q)
    {
        return -1;
    }

    //调用入队函数
    queue_push(Q, 111);
    queue_push(Q, 222);
    queue_push(Q, 333);
    queue_push(Q, 444);

    //调用遍历函数
    queue_show(Q);

    //调用出队函数
    queue_pop(Q);
    queue_pop(Q);
    
    //释放队列空间
    queue_destroy(Q);
    Q = NULL;


    
    return 0;
}

1.h

#ifndef LOOPLINKQUEUE_H
#define LOOPLINKQUEUE_H
#include<myhead.h>
//定义数据元素类型
typedef int datatype;

//定义结点类型
typedef struct Node
{
    union
    {
        datatype data;  //数据域
        int len;      //长度
    };

    struct Node *next;   //指针域
}Node, *NodePtr;

//定义队列类型
typedef struct 
{
    NodePtr head;    //头指针
    NodePtr tail;    //尾指针
}Queue, *QueuePtr;

//创建队列
QueuePtr queue_create();


//判空
int queue_empty(QueuePtr Q);

//入队
void queue_push(QueuePtr Q, datatype e);

//遍历队列
void queue_show(QueuePtr Q);

//出队
void queue_pop(QueuePtr Q);

//求队列长度
int queue_size(QueuePtr Q);

//销毁队列
void queue_destroy(QueuePtr Q);





#endif

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值