C++(队列)

分享内容

  1. 队列的概念
  2. 队列的实现
  3. 循环队列的实现

计算机存储设备

计算机中包括三种类型的存储设备:硬盘(hard disk)、内存(random-access memory,

RAM)、缓存(cache memory)。硬盘保存长期存储的数据,包括操作系统、程序、文件等。内存临时存储当前运行的程序和正在处理的数据。缓存存储经常访问的数据和指令,减少CPU访问内存的次数。

生活中的排队现象

排队时,新来的人不断加入队列尾部,而位于队列头部的人买好奶茶,逐个离开。遵守排队规则!先到的人先买奶茶!

我们将排队的人替换为各种类型的元素(如整数、字符、结构体等),就得到了队列这种数据结构。

队列的概念

● 队列(queue)是一种遵循"先进先出"规则的线性数据结构。

● 允许删除的一端称为"队头"(队首),允许插入的一端称为"队尾"

● 没有元素的队列,称为空队列

●队列中元素的个数就是队列长度

队列的操作

● 入队操作push():插入一个元素,成为新的队尾

● 出队操作pop():删除队首元素

队列的操作

● 入队操作push():插入一个元素,成为新的队尾

● 出队操作pop():删除队首元素

队列的操作-练习

1)已知队列中依次有这些元素:13、2、11、34、41、77、5、7、18、26、15,其

中13是队首,那么第五个出队列的元素是?B

A. 5

B. 41

C. 77

D. 13

其他常用的操作:

● 查询容量:

· 返回队列中元素的数量size()

· 查询队列是否为空empty()

● 元素访问:

· 查看队首元素front()

· 查看队尾元素 back()

队列

1具有两个端口的容器,一个端口负责进,另一个端口负责出。

2数据存取特点:先进先出。

队列与栈

我们已经学习过两种线性的数据结构 -- 栈和队列

栈:先进后出,只允许在一端进行操作。

队列:先进先出,允许在两端进行操作。

在实际应用中,选择哪种数据结构取决于问题的具体需求。例如,

· 在深度优先搜索算法中使用栈存储待访问的节点。

· 在广度优先搜索算法中用队列来遍历树或图的节点。

数据结构

链表、树、图

后面都会学到

数据结构可分为"线性"和"非线性"两大类:

● 线性数据结构:数据按照一定顺序排列,元素之间是一对一的顺序关系;

● 非线性数据结构:元素之间是一对多,或者多对多的关系。

队列的突现

● 通常用一个数组模拟一个队列,用两个变量标记队列的首尾

例如,实现一个最多可以存放7个整型元素的队列

① 创建长度为7的数组 int q7 = {0};

② 创建一个变量表示队头位置 int head = 0;

③ 创建一个变量表示队尾位置 int tail = 0;

初始时,head == tail,队列为空。

● 在数组上实现队列的常用操作

①入队操作:将x入队,即一个人来排队,要站在队尾。void push(int x)

队列未满时,直接把x赋给队尾,再让队尾指针tail+1。

cpp 复制代码
void push (int x) {
	if(tail < 7)
		q[tail] = x;
		tail++;
	}
}

x的数据类型要与队列元素类型一致

② 出队操作:删除队首。void pop()

队列不空时,队头指针head后移即可。

cpp 复制代码
void pop () {
	if(head != tail){
		head++;
	}
}

· 返回值类型void,只删除队头,不返回任何值
· 想获得队头可以用查询队头函数front()

③查询队头元素:int front()

队列不空时,返回队头元素,即qhead

【注意】front()只查看不删除,指针不动。

cpp 复制代码
int front(){
	if(head != tail)
		return q[head];
	cout << "Queue is empty";
	return -1;
}

· 返回值类型要与队列元素类型一致

好习惯:每次调用front()和pop()之前,都要用empty()检查一下,保证队列不为空。

④查询队尾元素:看一下谁排在最后一个。int back()

队列不空时,返回队尾元素,即qtail-1

cpp 复制代码
int back () {
	if(head != tail)
		return q[tail-1];
	cout << "Queue is empty";
	return -1;
}

· 返回值类型要与队列元素类型一致

⑤询问队列长度:int size()

cpp 复制代码
队列元素个数=tail-head

	int size() {
		return tail-head;
	}

⑥队列是否为空:bool empty()

cpp 复制代码
队列空的条件是:tail == head

bool empty() {
	return head == tail? true:false;
}

操作队列

输入7个整数,将这7个整数进行入队,接下来做三次出队操作,按照出队顺序输出出队元素,以上操作完成后输出此时的队首元素。

【输入格式】输入7个整数,用空格隔开。

【输出格式】输出2行,第1行输出出队元素,按照出队顺序输出,用空格

隔开。第2行输出完成出队操作后的队首元素。

【输入样例】4 3 9 5 12 6 7

【输出样例】4 3 9

5

1.完成队列的实现

  1. 队列是否为空empty()
  2. 入队函数 push()
  3. 出队函数 pop()
  4. 查询队首元素front()

2.完成主程序功能

  1. 输入7个整数,并依次入队
  2. 做3次出队操作,并输出
  3. 输出此时的队首元素

1.定义模拟队列的数组和变量(整数类型,数据长度是7)

cpp 复制代码
#define MAXN 7
int q[MAXN] = {0};
int head = 0;// 队头位置
int tail = 0;// 队尾+1的位置

2.实现 empty 函数

cpp 复制代码
bool empty () {
	return head == tail? true:false;
}

3.push函数 (从队尾入队)

cpp 复制代码
void push(int x) {
	if( tail <MAXN){// 队列不满
		q[tail] = x;
		tail++;
}

4.pop函数 (从队头出队)

cpp 复制代码
void pop () {
	if(!empty()){// 队列不空
	head++;
	}
}

5.front函数(查询队头)

cpp 复制代码
int front () {
	if( !empty())
		return q[head] ;
	cout << "Queue is empty";
	return -1;
}

好习惯:调用front()前,先查看队列是否为空,避免返回无效值。

cpp 复制代码
#include<iostream>
using namespace std;

#define MAXN 7
int q[MAXN] = {0};
int head= 0;//指向队头
int tail =0;//指向队尾下一个

bool empty () {
	return head == tail? true:false;
}
void push (int x) {
	if(tail <MAXN){// 队列不满
		q[tail] = x;
		tail++;
	}
}
void pop() {
	if(!empty()){//队列不空
		head++;
	}
}

int front () {
	if( !empty () )
		return q[head] ;
	cout << "Queue is empty";
	return -1;
}
int main() {
	int x;
	for(int i = 1;i <= 7;i++) {
		cin >> x;
		push (x) ;
	}
	for(int i = 1;i <= 3;i++) {
		cout << front() << '';
		pop();
	}
	cout << endl;
	cout << front () << '';
	return 0;
}

队列的问题

问题:n个元素,反复出队、入队,长度为n的数组实现的队列会不够用。

因为head和tail会一直向右移动,当它们到达数组尾部时就无法继续移动了。

为了解决此问题,我们可以将数组想象成首尾相接的"环形数组"。

循环队列指针的变化

循环队列空满的判断

循环队列元素个数

循环队列总结

● 通过循环队列优化了空间利用率

● 假设循环队列的数组长度为MAXN,最多能存储(MAXN-1)个元素

循环队列一练习

1)设循环队列的数组长度为n,其头尾指针分别为f和r,则队列中元素个数为?D

A, r-f

B. r-f+1

C. (r -f) % n

D. (r+n -f) % n

围圈报数

有n个人依次围成一圈,从第1个人开始报数,数到第m个人出列,然后从下一个人重新报数1,数到第m个人又出列,.,如此反复到所有的人全部出列为止。设n个人的编号分别为1、2、 ... 、n,打印出列的顺序。

【输入描述】两个数n、m,中间空格隔开。1≤n,m≤100。

【输出描述】出列顺序

【输入样例】4 17

【输出样例】1 3 4 2

  1. 可以用队列模拟这个过程:最初有n个人在队列中,报数1~m-1的人从队首跑到

    队尾(先出队再入队),报数m的人出队。

  2. 这个问题中每个人都有可能多次入队、出队,最好用循环队列,防止空间不够用。

实现循环队列

· 最多是n个人在队列中1≤n≤100

· 能放下n个人的循环队列,需要定义至少n+1长的数组

完整代码

cpp 复制代码
#include<iostream>
using namespace std;

#define MAXN 101
int q[MAXN]={0};//循环队列,最多能存储(MAXN-1)个元素
int head=0;//指向队头
int tail=0;//指向队尾下一个

bool empty() {
	return head == tail? true: false;
}
void push (int x) {
	if((tail+1) % MAXN != head){// 队列不满
		q[tail] = x;
		tail = (tail+1) % MAXN;
	}
}
void pop() {
	if(!empty()){//队列不空
		head = (head+1) % MAXN;
	}
}
int front () {
	if( !empty () )
		return q[head] ;
	cout << "Queue is empty";
	return -1;
}
int main(){
	int n,m;
	cin >> n >> m;
	for(int i=1;i <= n;i++){//n个编号依次入队
		push (i) ;
	}
	// 开始报数,直到队列为空
	int cnt=1;//报的数
	while(!empty () ) {
		if(cnt == m){//数到m,队首出队并输出
			cout << front() << " ";
			pop() ;
	}
	else[//其他数,队首出队,再入队(到队尾)
		int x =front();// 队首
		pop() ;
		push (x) ;
	}
	cnt++;// 1~m循环报数
	if (cnt == m+1)
		cnt = 1;
	}	
	return 0;
}

本次课程的知识点

  1. 队列的现实场景和概念

  2. 队列的操作

  3. 队列的实现

  4. 循环队列的实现

    1、以下不是队列的基本操作的是(B)?

A、从队尾插入一个新元素

B、从队列中删除队尾元素

C、判断一个队列是否为空

D、读取队首元素的值

2、若用一个大小为6的数组来实现循环队列,当前tail和head的值分别为0和3。

当从队列中删除一个元素,再加入两个元素后,tail和head的值分别为:B

A、1和5

C、4和2

B、2和4

D、5和1

密码规则

密码由生日作为基数(8位数字),生成密码的规则如下:

  1. 将第1个数字删除,第2个数字放到数字序列末端,再将第3个数字放至
    数字序列末端。
  2. 重复执行第一步,直到所有数字删除完毕。
  3. 删除的数字将会组成一串新的数字,这就是密码。
    【输入格式】一行,一组八位数字。
    【输出格式】一行,八位数字生成的密码。
    【输入样例】18781205
    【输出样例】18075281
cpp 复制代码
#include<iostream>
using namespace std;

#define MAXN 9
int q[MAXN]={0};//循环队列,最多能存储(MAXN-1)个元素
int head= 0;// 指向队头
int tail= 0;//指向队尾下一个

bool empty() {
	return head == tail? true: false;
}
void push (int x) {
	if((tail+1) % MAXN != head ){//队列不满
		q[tail] = x;
		tail = (tail+1) % MAXN;
	}
}

void pop () {
	if(!empty()){//队列不空
		head = (head+1) % MAXN;
	}
}

int front () {
	if( !empty () )
		return q[head] ;
	cout << "Queue is empty";
	return -1;
}
int main() {
	char ch[9];
	cin >> ch;
	for(int i=0;i <= 7;i++){//8个数字依次入队
		push (ch[i]-'0');
	}
	//开始报数1~3,直到队列为空
	int cnt= 1;//报的数
	while(!empty () ) {
		if(cnt == 1){//将第1个数字删除
			cout << front();
			pop() ;
		}
		else{ //第2、3个数字放到数字序列末端
			int x = front () ; // MM
			pop() ;
			push (x) ;
		}
		cnt++;// 1~3循环报数
		if (cnt == 4)
			cnt = 1;
	}
	return 0;
}