队列的基本认识和操作

队列的基本认识和操作

一.队列的基础知识

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 <stdio.h>
#include <stdlib.h>
#include <malloc.h>

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 <stdio.h>
#include <malloc.h>
#include <stdbool.h>

//定义一个队列的结构体
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;
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值