队列的简单理解和基本操作

一、 队列

  • 1. 队列的基本概念
    • 1.1 队列的定义
    • 1.2 队列的特点和说明
  • 2. 队列的顺序存储结构
    • 2.1 队列的顺序存储实现
      • 2.1.1 队列的顺序存储的定义
      • 2.1.2 队列的顺序存储类型描述
      • 2.1.3 队列顺序存储的约定及说明
    • 2.2 循环队列
      • 2.2.1 循环队列的原理
      • 2.2.2 循环队列的说明
      • 2.2.3 循环队列的基本操作
        • 2.2.3.1 循环队列的初始化
        • 实现思路
        • 实现代码
        • 2.2.3.2 判断循环队列是否为空
        • 实现思路
        • 实现代码
        • 2.2.3.3 循环队列入队
        • 实现思路
        • 实现代码
        • 2.2.3.4 循环队列出队
        • 实现思路
        • 实现代码
  • 3. 队列的链式存储结构
    • 3.1 队列的链式存储的定义
    • 3.2 链式队列的说明
    • 3.3 队列的链式存储类型描述
    • 3.4 链式队列的相关操作
      • 3.4.1 判断链式队列是否为空
        • 实现思路
        • 实现代码
      • 3.4.2 链式队列入队
        • 实现思路
        • 实现代码
      • 3.4.3 链式队列出队
        • 实现思路
        • 实现代码
  • 4. 双端队列
    • 4.1 普通双端队列
      • 4.1.1 普通双端队列的定义
      • 4.1.2 普通双端队列的说明
    • 4.2 输出受限的双端队列
    • 4.3 输入受限的双端队列

1. 队列的基本概念

1.1 队列的定义

只允许在一端插入,在另一端删除

1.2 队列的特点和说明

  • 队列是一种操作受限的线性表
  • 队头(首):允许删除的一端
  • 队尾:允许插入的一端
  • 空队列:没有元素的队列

2. 队列的顺序存储结构

2.1 队列的顺序存储实现

2.1.1 队列的顺序存储的定义

分配一块连续的存储单元存放队列的元素

2.1.2 队列的顺序存储类型描述

#define MaxSize 100   	//动态数组的最大容量 
typedef int ElemType;
typedef  struct{
	ElemType data[MaxSize];		//用一维数组存放队列元素 
	int front,					//队头指针 
		rear;					//队尾指针 
}SqQueue; 

2.1.3 队列顺序存储的约定及说明

  • front指针指向队头元素
  • rear指针指向队尾元素的下一个位置
  • 空队时:rear == front
  • 队列初始化:rear = front = 0
  • 入队:队未满时,先送值到队尾,再队尾指针加一
  • 出队:队为空时,先取队头元素,再队头指针加一
  • 不能使用q.rear == MaxSize 判断队满,因为出队的过程中,数组的前面部分会有空闲,这时候入队会有上溢出,但是是假溢出

队列的简单理解和基本操作_第1张图片

2.2 循环队列

2.2.1 循环队列的原理

将队列从逻辑上造成一个环状,利用取余实现

2.2.2 循环队列的说明

  • 初始(空队):q.front = q.rear = 0
  • 出队指针变化:q.front = (q.front+1)%MaxSize
  • 入队指针变化:q.rear = (q.read+1)%MaxSize
  • 队列长度:[MaxSize - (q.front - q.rear)]%MaxSize
    • rear在front后面:q.read - q.front
    • rear在front前面:MaxSize - (q.front - q.rear)
  • 出队、入队:都按顺时针,都是指针加一
  • 队满:
    • 方式一:牺牲一个存储的单元来区分空队、满队

      • 约定:当队头指针在队尾指针的下一个位置时,队满
      • 队满:(q.read + 1)%MaxSize == q.front
      • 队空:q.front == q.rear
      • 队列中的元素个数:(q.rear - q.front + MaxSize) % MaxSize
    • 方式二:在类型中新增一个数据成员,用与表示元素个数

      • 队空:q.Size == 0
      • 队满:q.Size == q.MaxSize
    • 方式三:在类型中新增一个数据成员,用以区分队空、队满

      • q.tag == 0:队空
      • q.tag == 1:队满

队列的简单理解和基本操作_第2张图片

2.2.3 循环队列的基本操作

2.2.3.1 循环队列的初始化

实现思路

 将队头、队尾都置为0

实现代码

//初始化循环队列 
void InitQueue(SqQueue *q){
	//初始化队列,设置队头指针和队尾指针为 0 
	q->rear = q->front = 0;
}

2.2.3.2 判断循环队列是否为空

实现思路

队头队尾指针相等,则队空

实现代码

//判断循环队列是否为空
int IsEmpty(SqQueue q){
	//判断对队头指针和队尾指针是否相等 
	if(q.rear == q.front){
		return 0;
	}
	else{
		return 1;
	}
} 

2.2.3.3 循环队列入队

实现思路

 1. 先判断队列是否队满
 2. 循环在队尾入队

实现代码

//入队
int EnQueue(SqQueue *q,int n) {
	
	int i;
	
	//判断是否已满
	if((q->rear+1)%MaxSize == q->front){
			return 0;
	}
	
	for(i = 0;i<n;i++){
		//入队 
		scanf("%d",&q->data[q->rear]);
		//队尾指针加 1 
		q->rear = (q->rear+1)%MaxSize;
	}
}

2.2.3.4 循环队列出队

实现思路

 1. 先判断队列是否是队空
 2. 循环方式进行出队

实现代码

//出队
int DeQueue(SqQueue *q){
	
	int x;
	
	//判断队空 
	if(q->rear == q->front){
		return 0;
	}
	
	while(q->rear != q->front){
		//获取队头元素 
		x = q->data[q->front];
		printf("%d ",x);
		//队头指针加 1 
		q->front = (q->front+1)%MaxSize;
	}
}

3. 队列的链式存储结构

3.1 队列的链式存储的定义

队列的链式表示成为链式队列

3.2 链式队列的说明

  • 链队本质上是一个同时带有队头指针、队尾指针的单链表
  • 头指针指向队头结点
  • 尾指针指向队尾结点
  • 链式队列适合于数据元素变化较大的情形,不存在上溢
  • 当使用多个队列时,最好使用链式队列,可以避免存储分配不合理下的溢出问题。

3.3 队列的链式存储类型描述

//链式队列的结点 
typedef struct LinkNode{
	ElemType data;				//数据域 
	struct LinkNode *next;		//指针域,指向下一个结点 
}LinkNode;
//链式队列 
typedef struct {
	LinkNode 	*front,			//队列的队头指针 
				*rear;			//队列的队尾指针 
}LinkQueue;

3.4 链式队列的相关操作

3.4.1 判断链式队列是否为空

实现思路

判断q.front == q.rear

实现代码

//判断链式队列是否为空 
int IsEmpty(LinkQueue q){
	if(q.front == q.rear){
		return 1;
	}
	else{
		return 0;
	}
}

3.4.2 链式队列入队

实现思路

1. 循环建立新结点
2. 向新结点的数据域赋值
3. 将其插入到队列的队尾

实现代码

//入队
void EnQueue(LinkQueue *head, int n) {

	int i;
	//循环向队列中添加元素 
	for(i=0;i<n;i++){
		//创建一个新的结点 
		LinkNode* qNode = (LinkNode*)malloc(sizeof(LinkNode));
		//向新结点的数据域赋值 
		scanf("%d",&qNode->data);
		qNode->next = NULL;
		//插入队尾 
		head->rear->next = qNode;
		//更新rear 
		head->rear = qNode;
	}
}

3.4.3 链式队列出队

实现思路

 1. 先判断是否是空队
 2. 获取队首结点的数据
 3. 从队列上摘除该结点

实现代码

// 出队列 
int Delete(LinkQueue *head,int n) {
	int i;
	if (head == NULL || head->front == head->rear) {
		return 0;
	}
	
	for(i=0;i<n;i++){
		// 获取对队首结点 
		LinkNode *temp = head->front->next;
		//获取队首结点的数据 
		int val = temp->data;
		printf("%d ",val);
		//从队列上摘除该结点 
		head->front->next = temp->next;
		
		//判断该结点是否是最后一个结点 
		if (temp == head->rear) {
			//将队列置空 
			head->rear = head->front;
		}
		
		//释放结点 
		free(temp);
	}
}

4. 双端队列

4.1 普通双端队列

4.1.1 普通双端队列的定义

双端队列指允许两端都可以进行入队、出队操作的队列

4.1.2 普通双端队列的说明

  • 双端队列的逻辑特性仍然是线性结构
  • 两端都可以出队、入队
    队列的简单理解和基本操作_第3张图片

4.2 输出受限的双端队列

允许在一端进行插入、删除,但在另一端只允许插入的双端队列

队列的简单理解和基本操作_第4张图片

4.3 输入受限的双端队列

允许在一端进行插入、删除,但在另一端只允许删除的双端队列

队列的简单理解和基本操作_第5张图片

源码下载

关注我的技术公众号,时常会有优质的技术文章推送。
微信扫一扫下方二维码即可关注:
队列的简单理解和基本操作_第6张图片

你可能感兴趣的:(数据结构,队列,数据结构,c语言)