3-2 队列

时间:2022-06-24
本文章向大家介绍3-2 队列,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
3-2 队列

1、基本概念

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行删除操作的端称为队头 ,进行插入操作的端称为队尾

FIFO (First In First Out) 先进先出

队列的基本操作:

①init() 置空,将队列Q初始化为空;

②empty() 判队列空,

③size() 返回队列中元素的个数

④front() 取队头元素,若队列未空,则函数返回队头 数据元素。

⑤rear() 取尾头元素,若队列未空,则函数返回队尾 数据元素。

⑥push(x) 入队列,若队列未满,在原队尾后加入数据元素x,使x成为新的队尾元素;

⑦pop() 出队列,若队列未空,则将队列的队头元素删除。

队列存储结构的实现有以下两种方式:顺序队列 、 链式队列

2、顺序队列

在顺序表的基础上实现的队列结构;

一般情况下,rear等于max时,说明队尾元素应占据了顺序表的最后一个存储位置,即无法再从队尾插入新元素,

这个时候判定为“队满”,但是如果说队首元素前面还有空的位置,那么说明我们这个顺序表还是有空间可以存放元素的,

所以rear == max 称为 “假溢出”那么只要改动一下判别“队满”的条件,就可以把顺序队列 当作 循环队列 来使用了:

①头文件:

#ifndef SQ_QUEUE_H_
#define SQ_QUEUE_H_
#define MAX 10

struct sq_queue {
	int data[MAX];
	int front;//队首标记
	int rear;//队尾标记
};

void Init(sq_queue *q);
bool Empty(sq_queue *q);
int Len(sq_queue *q);

bool Full_SQ(sq_queue *q);
bool Full_Circle(sq_queue *q);

int Front(sq_queue *q);
int Rear(sq_queue *q);

int Pop(sq_queue *q);
void Push_sq(sq_queue *q, int x);
void Push_circle(sq_queue *q, int x);

void Show(sq_queue *q);

#endif // !SQ_QUEUE_H_

②函数定义文件:

#include<iostream>
#include"sq_queue.h"

using std::cin;
using std::cout;
using std::endl;

/*初始化*/
void Init(sq_queue *q) {
	q->front = q->rear = 0;
}

/*判空*/
bool Empty(sq_queue *q) {
	if (q->front == q->rear)
		return true;
	else
		return false;
}

/*返回长度*/
int Len(sq_queue *q) {
	//这个公式对于循环队列 和 非循环普通队列都适用
	return (q->rear - q->front + MAX) % MAX;
}


/*普通顺序队列的 判满条件, 会有假溢出现象*/
bool Full_SQ(sq_queue *q) {
	if (q->rear == MAX)
		return true;
	else
		return false;
}

/*循环顺序队列的 判满条件, 能利用front前面的空间存储元素*/
bool Full_Circle(sq_queue *q) {

	if ((q->rear + 1) % MAX == q->front)
		return true;

	else
		return false;
}

int Front(sq_queue *q) {
	if (Empty(q))
		return NAN;
	else
		return q->data[q->front];
}

int Rear(sq_queue *q) {
	if (Empty(q))
		return NAN;
	else
		return q->data[q->rear-1];//由于rear指向队尾元素的下一个位置,所以这里要 -1
}

int Pop(sq_queue *q) {
	if (Empty(q))
		return NAN;
	else
		return q->data[q->front++];
}

void Push_sq(sq_queue *q, int x) {
	/*普通顺序队列的情况*/
	if (Full_SQ(q))
	return;
	else
	q->data[q->rear++]=x;
	
}
void Push_circle(sq_queue *q, int x){
	/*循环队列的情况*/
	if (Full_Circle(q))
		return;
	else {
		q->rear = q->rear % MAX;
		q->data[q->rear] = x;
		q->rear++;
	}
}


void Show(sq_queue *q) {
	if (Empty(q))
		return;
	else {
		for (int i = q->front; i < MAX; i++)
			cout << q->data[i] << "   ";

		if (q->rear < q->front)
			for (int j = 0; j < q->rear; j++)
				cout << q->data[j] << "   ";
	}
	cout << endl;
}

③主函数文件:

#include<iostream>
#include"sq_queue.h"

using std::cin;
using std::cout;
using std::endl;

int main() {

	sq_queue q1;
	Init(&q1);
	cout << "nthe queue is empty: " << Empty(&q1) << " and its length is: " << Len(&q1) << endl;

	int i = 0;
	cout << "n先将所有位置都填上元素:n";
	
	//先用顺序队列的判满条件
	while (!Full_SQ(&q1)) {
		Push_sq(&q1, i);
		i++;
		//cout << q1.front <<"  "<<q1.rear<< endl;
		//getchar();
	}
	Show(&q1);

	cout << "n然后将队首的3个元素依次出队列:n";
	Pop(&q1); Show(&q1);
	Pop(&q1); Show(&q1);
	Pop(&q1); Show(&q1);

	cout << "n根据普通顺序队列的判满条件,它是一个满队列:Full_SQ =" << Full_SQ(&q1) << endl;
	cout << "n根据循环队列的判满条件,它不是一个满队列:Full_Circle = " << Full_Circle(&q1) << endl;

	cout << "n所以如果使用循环队列的判满条件,我们可以继续入队列,添加元素:n";
	//再用循环队列的判满条件
	while (!Full_Circle(&q1)) {
		Push_circle(&q1, i);
		i++;
	}
	Show(&q1);
	cout << "n但是能存储的元素的数目要比普通顺序队列少一个n";
	cin.get();
	return 0;
}

3、链式队列

采用链表来实现队列,我还是选用带头结点的单链表来实现,因为其在第一个位置删除时,不用改变头指针;

我们在插入的时候,选择在链表尾部使用尾插法插入,所以链表尾部视为队尾rear;

在删除时,删除头结点后的首结点,为了方便起见,我们可以把头结点认为是front,这样front就一直不会变化,比较方便

如图所示:

①头文件

#ifndef LINK_QUEUE_H_
#define LINK_QUEUE_H_

/**/
/**/
/*结点的定义*/
typedef struct Link_list_node {
	int data;
	struct Link_list_node *next;
}node;

/*链式队列结构定义*/
typedef struct link_queue {
	node * front;
	node * rear;
}lq;

void Init(lq * q);
bool Empty(lq * q);
void Destroy(lq *q);

int Size(lq *q);
int Front(lq *q);
int Rear(lq *q);

int Pop(lq *q);
void Push(lq *q, int x);

void Show(lq *q);
#endif // !LINK_QUEUE_H_

②函数定义文件

#include<iostream>
#include"link_queue.h"
using std::cin;
using std::cout;
using std::endl;

void Init(lq * q) {
	/**/
	q->front = new node;
	q->front->next = nullptr;
	q->rear = q->front;
}


bool Empty(lq * q) {
	if (q->front == q->rear)
		return true;
	else
		return false;
}

void Destroy(lq *q) {
	node *p;
	while (q->front != nullptr) {
		p = q->front->next;
		delete q->front;
		q->front = p;
	}
	q->rear = nullptr;
}

int Size(lq *q) {
	node *p = q->front;
	int j = 0;
	while (p->next != nullptr) {
		p = p->next;
		j++;
	}
	return j;
}

int Front(lq *q) {
	if (Empty(q))
		return NAN;
	else
		return q->front->next->data;
}

int Rear(lq *q) {
	if (Empty(q))
		return NAN;
	else {
		node *p = q->front;
		while (p->next != nullptr)
			p = p->next;
		return p->data;
	}
}

int Pop(lq *q) {
	if (Empty(q))
		return NAN;
	else {
		//创建一个结点指向队首元素
		node *p = q->front->next;
		//将队首元素从链表中断开
		q->front->next = p->next;
		//存储此队首元素的值
		int x = p->data;
		//如果删除的这第一个元素恰好是rear,就需要更新rear的值为front,即空队列的情况
		if(p==q->rear)
			q->rear = q->front;

		delete p;
		return x;
	}
}

void Push(lq *q, int x) {
	//创建一个结点接受新的值
	node *s = new node;
	s->data = x;
	s->next = nullptr;

	//将其插入链表的最后1个位置,即采用尾插法
	/*传统思路是通过遍历的方式找到最后一个结点*/
/*	
	node *p = q->front;
	while (p->next != nullptr) {
		p = p->next;
	}
	p->next = s;
*/
	/*但是我们这里已经有最后一个结点的地址了,那就是rear!*/
	q->rear->next = s;;
	q->rear = s;

}

void Show(lq *q) {
	node*p = q->front;
	while (p->next != nullptr) {
		p = p->next;
		cout << p->data << "   ";
	}
	cout << endl;
}

③主函数文件

#include<iostream>
#include"link_queue.h"
using std::cin;
using std::cout;
using std::endl;

int main() {

	lq q1;
	Init(&q1);
	cout << "nthe queue is empty: " << Empty(&q1) << " and its length is: " << Size(&q1) << endl;

	int x = 0;
	cout << "n在队尾插入元素,按 q 表示结束:n";
	while (cin>>x) {
		Push(&q1, x);
		cout << "the queue is: ";
		Show(&q1);
	}
	cin.clear();
	while (cin.get() != 'n')
		continue;

	cout << "n在队头弹出元素n";
	while (!Empty(&q1)) {
		cout<<"ndelete: "<<Pop(&q1);
		cout << "nthe queue is: ";
		Show(&q1);
	}

	Destroy(&q1);
	cin.get();
	return 0;
}

如有错误,请不吝指出,谢谢