博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
队列的基本认识和操作
阅读量:179 次
发布时间:2019-02-27

本文共 9863 字,大约阅读时间需要 32 分钟。

队列的基本认识和操作

一.队列的基础知识

1.定义:

队列是一种可以实现 “先进先出” 的存储结构。 也可以理解为一种特殊的链表,它只允许在链表的一端进行插入,在链表的另一端删除元素,不可以在中间操作。如同我们平常去排队买票,先到的先买到票就可以先走,后来的就只能等轮到自己时才可以买票走。

2.队列的分类

队列的分类

链式队列和静态队列的大体相同,不过实现方法不同。

队列的结构图:

链表队列

静态队列

对于队列也是受限的线性表,但是由于顺序存储会出现假溢出,所以出现了循环队列的结构防止顺序存储的队列出现溢出假现象,链队列不会出现这种情况。

3.队列通常定义的参数:

①.一个队列通常由 队头(front)队尾(rear) 两个参数确定。

②.两个参数不同场合的不同含义:

  • 队列初始化
    front 和 rear 的值都是零。
  • 队列非空
    front 代表的是队列的第一个元素。
    rear 代表的是队列的最后一个有效元素的下一个元素。
  • 队列为空
    front 和 rear 的值相等,但不一定是零。

二.链式队列的建立和基本操作

1.队列所需的结构体

//链式队列结点typedef struct Node {
int data; //数据域 struct Node *pNode; //指针域}NODE, *PNODE;//链式队列typedef struct awsl {
PNODE front; //指向队头 PNODE rear; //指向队尾}AWSL, *PAWSL;

2.队列初始化

链式队列就像链表,所以就要初始化的时候动态开辟内存或者用数组的形式为其开辟内存,除了要开辟内存外,还要初始化结构中的其他项,尤其注意指针域,不初始化可能会对计算机已存储的数值产生影响。当 队头(front)和 队尾(rear)相等时表示队列为空,即 队头(front)和 队尾(rear)指向同一个结点。

//队列初始化void init_awsl(PAWSL ps) {
PNODE t = (PNODE)malloc(sizeof(NODE)); //请求计算机分配一个动态的内存,作为队头 if (NULL == t) {
printf("内存不足!1\n"); exit(-1); } //辨别计算机是否有足够的内存分配给该程序,作为队列结点存储数据 ps -> rear = ps -> front = t; //令队头和队尾都指向新创建的结点,便于后续的入队和出队 t -> pNode = NULL; //使结点初始化,避免成为野指针,影响程序数据的存储 return;}

3.入队

入队操作只能从队尾, 链式队列不用担心队列是否已满,但是我们先要保证内存空间要有,所以就要先判断计算机是否有足够的空间分配给该函数,然后就是尾插,先使队尾(rear)指向的结点指向新创建的结点,再使 队尾(rear)为新插入的结点。

//入队void push_awsl(PAWSL ps) {
if (NULL != ps -> rear -> pNode) {
printf("请初始化!\n"); return; } //判断队尾结点是否初始化,防止其成为野指针对程序造成影响 PNODE t = (PNODE)malloc(sizeof(NODE)); //请求计算机分配一个动态的内存,作为新入列的结点 if (NULL == t) {
printf("内存不足!\n"); exit(-1); } //辨别计算机是否有足够的内存分配给该程序,作为队列结点存储数据 printf("要入列的元素是:"); scanf("%d", &ps -> rear -> data); //输入新插入的结点存储的数据 ps -> rear -> pNode = t; //将原队尾结点指向新创建的结点,使新创建的结点作为队尾 ps -> rear = t; //将 原队尾(rear)指向新创建的结点 t -> pNode = NULL; //使新创建的结点指向为空 return;}

4.出队

出队操作只能从队头出队,对于链式队列只需要将 队头(front)指向下一个结点再释放掉原 队头(front)位置的空间即可。

//出队void go_awsl(PAWSL ps) {
PNODE t = ps -> front; //定义一个该队列结构体类型的指针指向队头(front),便于对队头元素的出列操作 if ((NULL == t -> pNode) || (NULL != ps -> rear -> pNode)) {
printf("队列为空!出列失败。\n"); return; } //判断原队列是否为空 ps -> front = t -> pNode; //将原队头(front)指向下一个结点,从而达到使原队头数据出列的效果 printf("出列的元素是;%d\n", t -> data); free(t); //释放计算机分配给原队头的内存 return;}

5.遍历队列

对于队列的遍历,只需要定义一个指针从 队头(front)到 队尾(rear)将每个结点存储的数据逐一输出即可。

//遍历队列void traverse_awsl(PAWSL ps) {
PNODE t = ps -> front; //定义一个该队列结构体类型的指针指向队头,使其在队列中移动遍历队列 if ((NULL == t -> pNode) || (NULL != ps -> rear -> pNode)) {
printf("队列为空!遍历失败。\n"); return; } //判断队列是否为空,为空则不需要遍历 printf("队列内的元素:\n"); while (t != ps -> rear) {
printf("%d\n", t -> data); t = t -> pNode; } //让新定义的指针t从 队头(front)到 队尾(rear)遍历队列 return;}

6.清空队列

从 队头(front)开始直到 队尾(rear),队头(front)每往后走一个,就释放掉 队头(front)原指向的结点即可。

//清空void empty_awsl(PAWSL ps) {
if (NULL != ps -> rear -> pNode) {
printf("请初始化!\n"); return; } //判断队尾结点是否初始化,防止其成为野指针对程序造成影响 while (ps -> front != ps -> rear) {
PNODE t = ps -> front; ps -> front = t -> pNode; free(t); } //将数据从 队头(front)到队尾(rear)逐一释放 printf("已清空!\n"); return;}

7.链式队列源代码.

#include 
#include
#include
typedef struct Node{
//节点 int data; struct Node *pNode;}NODE, *PNODE;typedef struct awsl {
//链式队列 PNODE front; PNODE rear;}AWSL, *PAWSL;void init_awsl(PAWSL);//初始化void push_awsl(PAWSL);//入列void go_awsl(PAWSL);//出列void traverse_awsl(PAWSL);//遍历void empty_awsl(PAWSL);//清空//主函数int main(void) {
AWSL S; init_awsl(&S); push_awsl(&S); push_awsl(&S); push_awsl(&S); go_awsl(&S); traverse_awsl(&S); empty_awsl(&S); traverse_awsl(&S); return 0;}//初始化void init_awsl(PAWSL ps) {
PNODE t = (PNODE)malloc(sizeof(NODE)); if (NULL == t) {
printf("内存不足!1\n"); exit(-1); } ps -> rear = ps -> front = t; t -> pNode = NULL; return;}//入列void push_awsl(PAWSL ps) {
if (NULL != ps -> rear -> pNode) {
printf("请初始化!\n"); return; } PNODE t = (PNODE)malloc(sizeof(NODE)); if (NULL == t) {
printf("内存不足!\n"); exit(-1); } printf("要入列的元素是:"); scanf("%d", &ps -> rear -> data); ps -> rear -> pNode = t; ps -> rear = t; t -> pNode = NULL; return;}//出列void go_awsl(PAWSL ps) {
PNODE t = ps -> front; if ((NULL == t -> pNode) || (NULL != ps -> rear -> pNode)) {
printf("队列为空!出列失败。\n"); return; } ps -> front = t -> pNode; printf("出列的元素是;%d\n", t -> data); free(t); return;}//遍历void traverse_awsl(PAWSL ps) {
PNODE t = ps -> front; if ((NULL == t -> pNode) || (NULL != ps -> rear -> pNode)) {
printf("队列为空!遍历失败。\n"); return; } printf("队列内的元素:\n"); while (t != ps -> rear) {
printf("%d\n", t -> data); t = t -> pNode; } return;}//清空void empty_awsl(PAWSL ps) {
if (NULL != ps -> rear -> pNode) {
printf("请初始化!\n"); return; } while (ps -> front != ps -> rear) {
PNODE t = ps -> front; ps -> front = t -> pNode; free(t); } printf("已清空!\n"); return;}

三.静态队列的建立和基本操作

创建静态队列的前提条件:

所谓的静态队列就是一个循环数组,但是数组是一个线性关系的顺序表,实现数组的循环是静态队列的关键,这就使我们想到了约瑟夫环,利用 % 的原理使数组变成一个环型,即 队头(front)和 队尾(rear)每次移动时并不是向数组一般直接 + 1,而是利用 front = (front + 1)% 数组长度 和 rear = (rear + 1)% 数组长度 来实现环形数组

使用此方法需要注意的是,顺序队列在判断数组是否已满时,出现下面情况:

  • 当队列为空时,队列的头指针等于队列的尾指针;
  • 当数组满员时,队列的头指针等于队列的尾指针;

顺序队列的存储状态不同,但是判断条件相同。

①为了对其进行区分,最简单的解决办法是: 牺牲掉数组中的一个存储空间.
②判断数组满员的条件是: 尾指针的下一个位置和头指针相遇,就说明数组满了.

1.队列所需的结构体

//定义一个静态队列的结构体typedef struct queue {
int *pBase; //用于指向定义的数组 int front; //指向队头 int rear; //指向队尾}QUEUE;

2.队列初始化

静态队列就像数组一样,不过他是一个循环数组,因为需要在被调中定义静态队列,所以需要动态分配内存,除了要开辟内存外,还要初始化结构中的其他项当 队头(front)和 队尾(rear)相等时表示空,即两数值相同

//初始化队列void init(QUEUE *pQ) {
pQ -> pBase = (int *)malloc(sizeof(int) * 6); //请求系统分配6个整型单元的空间,这里的6可以随便改为自己需要的数组长度。 pQ -> front = 0; //初始化 队头(front),使它表示数组的下标为0 pQ -> rear = 0; //初始化 队尾(rear),使它表示数组的下标为0}

3.判断队列是否存满

当 队尾(rear)的下一个就是 队头(front)时,表示该队列已满

//判断队列是否存满bool full_queue(QUEUE *pQ) {
if ((pQ -> rear + 1) % 6 == pQ -> front) {
return true; } else {
return false; }}

4.入队

入队操作只能从队尾,入队我们先要保证内存空间要有,所以就要先判断队列是否已满,然后就是尾插,rear表示的是队尾的存储值的空间的下一个,也就是直接把值存进下标为 rear 的空间.。

//入队bool en_queue(QUEUE *pQ, int val) {
if (full_queue(pQ)) {
//判断队列是否已满 return false; } else {
pQ -> pBase[pQ -> rear] = val; //将值赋给原下标为 rear 的单元 pQ -> rear = (pQ -> rear + 1) % 6; //再使 rear 的值为数组下一个单元的下标 return true; }}

5.判断队列是否为空

当静态队列的 队头(front)和 队尾(rear)值相等时,就表示该队列为空。

//判断队列是否为空bool empty_queue(QUEUE *pQ){
if (pQ -> rear == pQ -> front) {
return true; } else {
return false; }}

6.出队

静态队列的出队,只能从队头出队,对于静态队列,只需要修改 队头(front),使它为下一个单元的下标就可以,这样就可以把数据从逻辑上删除。

//出队bool out_queue(QUEUE *pQ, int *val) {
if (empty_queue(pQ)) {
//判断队列是否为空 return false; } else {
*val = pQ -> pBase[pQ -> front]; //将要出队的值赋给 val 指向的地址 pQ -> front = (pQ -> front + 1) % 6; // 再使 front 的值为数组下一个单元的下标 return true; }}

7.遍历队列

定义一个整型类型的变量,作为下标从 队头(front)遍历到 队尾(rear)并逐一输出。

//遍历队列void traverse_queue(QUEUE *pQ) {
int i = pQ -> front; //i 用于遍历静态队列(循环数组) while (i != pQ -> rear) {
printf("%5d", pQ -> pBase[i]); i = (i + 1) % 6; } //从下标为 front 开始到下标为 rear 的单元,逐一输出 return;}

8.清空队列

清空静态队列,我们只需要把 队头(front)和 队尾(rear)下标重新指向数组的0下标即可。

//清空队列bool clear_queue(QUEUE *pQ) {
pQ -> front = pQ -> rear = 0; return true;}

9.摧毁队列

对于摧毁操作,我们就要把初始化中开辟的内存释放掉并且把 队头(front)和 队尾(rear)赋值为0。

//摧毁队列bool destory_queue(QUEUE *pQ) {
free(pQ -> pBase); //释放动态数组 pQ -> pBase = NULL; //使原指向动态数组的指针指向为空,防止其成为野指针影响程序其他值得存储 pQ -> front = pQ -> rear = 0; //将 front 和 rear 都归零 return true;}

10.静态队列源代码

#include 
#include
#include
//定义一个队列的结构体typedef struct queue {
int *pBase; int front; int rear;}QUEUE;void init(QUEUE *); //初始化队列bool en_queue(QUEUE *, int); //入队bool full_queue(QUEUE *); //判断队列是否满void traverse_queue(QUEUE *); //遍历队列bool out_queue(QUEUE *, int *); //出队bool empty_queue(QUEUE *); //判断队列是否为空bool clear_queue(QUEUE *pQ); //清空队列bool destory_queue(QUEUE *pQ); //摧毁队列int main(void) {
QUEUE Q; int val; init(&Q); en_queue(&Q, 1); en_queue(&Q, 2); en_queue(&Q, 3); en_queue(&Q, 4); en_queue(&Q, 5); en_queue(&Q, 6); en_queue(&Q, 7); traverse_queue(&Q); printf("\n"); if (out_queue(&Q, &val)) {
printf("出队成功,出队的元素为%2d", val); } else {
printf("出队失败!"); } printf("\n"); traverse_queue(&Q); printf("\n"); if (clear_queue(&Q)) {
printf("队列已清空!"); } else {
printf("队列清空失败!"); } printf("\n"); if (destory_queue(&Q)) {
printf("队列摧毁成功!"); } else {
printf("队列摧毁失败!"); } return 0;}//初始化队列void init(QUEUE *pQ) {
pQ -> pBase = (int *)malloc(sizeof(int) * 6); pQ -> front = 0; pQ -> rear = 0;}//入队bool en_queue(QUEUE *pQ, int val) {
if (full_queue(pQ)) {
return false; } else {
pQ -> pBase[pQ -> rear] = val; pQ -> rear = (pQ -> rear + 1) % 6; return true; }}//判断队列是否满bool full_queue(QUEUE *pQ) {
if ((pQ -> rear + 1) % 6 == pQ -> front) {
return true; } else {
return false; }}//遍历队列void traverse_queue(QUEUE *pQ) {
int i = pQ -> front; while (i != pQ -> rear) {
printf("%5d", pQ -> pBase[i]); i = (i + 1) % 6; } return;}//出队bool out_queue(QUEUE *pQ, int *val) {
if (empty_queue(pQ)) {
return false; } else {
*val = pQ -> pBase[pQ -> front]; pQ -> front = (pQ -> front + 1) % 6; return true; }}//判断队列是否为空bool empty_queue(QUEUE *pQ){
if (pQ -> rear == pQ -> front) {
return true; } else {
return false; }}//摧毁队列bool destory_queue(QUEUE *pQ) {
free(pQ -> pBase); pQ -> pBase = NULL; pQ -> front = pQ -> rear = 0; return true;}//清空队列bool clear_queue(QUEUE *pQ) {
pQ -> front = pQ -> rear = 0; return true;}

转载地址:http://jfrf.baihongyu.com/

你可能感兴趣的文章