数据结构(5)--队列

一、队列的基本概念

        队列(Queue)也是一种操作受限的线性表,只允许在表的一端进行插入操作,而在另一端进行删除操作,跟我们日常生活中的排队是一致的,操作的特性是先进先出(FIFO)。

        允许插入(也称入队,进队)的一端称为队尾,允许删除(出队)的一端称为队头。

二、队列的顺序存储结构

2.1 顺序队列

2.1.1 定义说明

        队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针:队头指针front指向队头元素,队尾指针rear指向队尾元素的下一个位置(当然也可以指向队尾,根据自己的情况来即可)。

2.1.2 函数说明

顺序存储类型

typedef struct {
	Elemtype data[Maxsize]; //存放队列元素
	int front; //队-头指针
	int rear; //队-尾指针(我这里设置的是: 指向队尾元素的下一个位置)
}SqQueue;

初始化

bool InitQueue(SqQueue& Q)
{
	Q.front = 0;
	Q.rear = 0;
	return true;
}

入队

bool EnQueue(SqQueue& Q, Elemtype e)
{
	if (Q.rear == Maxsize)
		return false; //队满则报错
	else
	{
		Q.data[Q.rear] = e; //将元素送入队尾
		Q.rear = Q.rear + 1; //队尾指针加1,指向队尾的下一个位置
		return true;
	}
}

出队

bool DeQueue(SqQueue& Q, Elemtype& x)
{
	if (Q.rear == Q.front)
		return false; //队空则报错
	else
	{
		x = Q.data[Q.front]; //当前队头元素记为x
		Q.front = Q.front + 1; //队头指针加1,往后移
		return true;
	}
}

打印队列

void PrintQueue(SqQueue& Q)
{
	cout << "从队列到队尾的元素数据是:";
	for (int i = Q.front; i < Q.rear; i++)
	{
		cout << Q.data[i] << " ";
	}
	cout << endl;
}

主函数

#include<iostream>
using namespace std;
#define Elemtype int
#define Maxsize 10

int main()
{
	SqQueue Q;
	InitQueue(Q);
	int n;
	//(1)入队数据
	cout << "(最多10个)想要入队列几个数据:" << endl;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int j;
		cin >> j;
		EnQueue(Q, j);
	}
	PrintQueue(Q);
	//(2)出队
	int m;
	DeQueue(Q, m);
	PrintQueue(Q);
	cout << "出队元素值为:" << m << endl;

	system("pause");
	return 0;
}

2.1.3 思考小结

(1)可以看到:当Q.rear == Q.front成立,可以用来判断队列是否为空;

(2)进队操作:队不满时,先送值到队尾,再讲队尾指针加1;出队操作:队不空时,先送出队头元素,再讲队头指针加1;

(3)但能否用Q.rear==Maxsize来作为队列满的情况呢?即使我这里是用的这个,但其实显然不能这样做。假溢出:当元素被插入到数组的中下标的最大的位置上之后,队列的空间空间就用完了,尽管此时数组的低端还有空闲空间,也无法再入队新的元素了。(那该如何解决呢,就用到我们马上要介绍的循环队列了)

2.2 循环队列

2.2.1 定义说明

        我们在前面指出了顺序队列的缺点,那我们讲顺序队列臆造成一个环状的空间,把存储队列元素的表从逻辑上视为一个环。为达到循环的效果,我们使用“除法取余运算%”来实现。

        为了区分队空与队满的情况,有三种处理方式:

(1)牺牲一个队列单元,则队满的条件是:(Q.rear+1)%Maxsize==Q.front;队列中元素的个数是:(Q.rear - Q.front + Maxsize) % Maxsize。

(2)类型中增设表示元素个数的 size 数据成员。这样队空Q.size==0,队满Q.size==Maxsize;

(3)类型中增设 tag 数据成员,当tag==0时,若因为删除导致此时Q.rear == Q.front,则队空;tag==1时,若因为插入导致此时Q.rear == Q.front,则队满。

2.2.2 函数说明(我是用上述的第一种方法)

入队

bool EnQueue(SqQueue& Q, Elemtype e)
{
	if ((Q.rear + 1) % Maxsize == Q.front)
		return false; //队满则报错
	else
	{
		Q.data[Q.rear] = e; //将元素送入队尾
		Q.rear = (Q.rear + 1) % Maxsize; //队尾指针加1取模(达到循环的效果),指向队尾的下一个位置
		return true;
	}
}

出队

bool DeQueue(SqQueue& Q, Elemtype& x)
{
	if (Q.rear == Q.front)
		return false; //队空则报错
	else
	{
		x = Q.data[Q.front]; //当前队头元素记为x
		Q.front = (Q.front + 1) % Maxsize; //队头指针加1取模(达到循环的效果),往后移
		return true;
	}
}

打印

void PrintQueue(SqQueue& Q)
{
	int len = (Q.rear - Q.front + Maxsize) % Maxsize;
	cout << "从队头到队尾共有" << len << "个元素,分别是";
	for (int i = 0; i < len; i++)
	{
		int j = (Q.front + i) % Maxsize;
		cout << Q.data[j] << " ";
	}
	cout << endl;
}

主函数

int main()
{
	SqQueue Q;
	InitQueue(Q);
	//(1)入队数据
	cout << "入队9个数据(会少使用一个队列单元):" << endl;
	for (int i = 0; i < Maxsize-1; i++)
	{
		int j;
		cin >> j;
		EnQueue(Q, j);
	}
	PrintQueue(Q);
	//(2)出队
	int m;
	DeQueue(Q, m);
	PrintQueue(Q);
	cout << "出队元素值为:" << m << endl;
	//(3)再入队1个数据
	EnQueue(Q, 10);
	PrintQueue(Q);
	system("pause");
	return 0;
}

 

2.2.3 思考小结

        只有在入队,出队的操作上与顺序表有些区别,是为了达到循环的效果。只是逻辑上循环,实际上也还是一连段的空间。

三、队列的链式存储结构

3.1 定义说明

        队列的链式表示称为链队列,它实际上是一个同时带有队头指针和队尾指针的单链表。头指针指向队头结点,尾指针指向队尾结点。

 

3.2 函数说明

(1)链式存储类型描述

//链式存储类型
typedef struct LinkNode {       //链式队列结点
	Elemtype data;
	struct LinkNode* next;
}LinkNode;
typedef struct {               //链式队列
	LinkNode* front, * rear;   //队列的队头和队尾指针
}LinkQueue;

(2)初始化(带头结点)

//初始化
void InitQueue(LinkQueue& Q)
{
	LinkNode* p;
	p = new LinkNode;  //创建头结点
	if (!p)
	{
		cout << "分配内存失败" << endl;
		return;
	}
	p->next = NULL;
	Q.front = Q.rear = p; //队列的头指针和尾指针都指向头结点
}

(3)判断是否为空

//判断队列是否为空
bool IsEmpty(LinkQueue Q)
{
	if (Q.front == Q.rear)
		return true; //为空
	else
		return false;
}

(4)入队

//入队(只能从队尾插入)
void EnQueue(LinkQueue& Q, Elemtype e)
{
	LinkNode* p;
	p = new LinkNode;
	p->data = e;
	p->next = NULL;
	Q.rear->next = p;
	Q.rear = p;
}

(5)出队

//出队(只有队头元素出队)
bool DeQueue(LinkQueue& Q, Elemtype& e)
{
	if (IsEmpty(Q))
		return false; //队列为空
	LinkNode* p;
	p = Q.front->next;
	if (Q.front->next == Q.rear) //若原队列只有一个结点,删除后变空
		Q.front = Q.rear; 
	e = p->data;
	Q.front->next = p->next;
	delete p;
}

(6)打印

//打印队列
void PrintQueue(LinkQueue& Q)
{
	int length = 0;
	LinkNode* p = Q.front->next;
	cout << "此队列为:";
	while (p != NULL)
	{
		cout << p->data << " ";
		length++;
		p = p->next;
	}
	cout << "队列长度为" << length << endl;
}

(7)主函数

#include<iostream>
using namespace std;
#define Elemtype int


int main()
{
	LinkQueue Q;
	InitQueue(Q);
	//入队
	int n;
	cout << "想要入队列几个数据:" << endl;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int j;
		cin >> j;
		EnQueue(Q, j);
	}
	PrintQueue(Q);
	//出队
	int e;
	DeQueue(Q, e);
	cout << "队头元素" << e << "出队" << endl;
	PrintQueue(Q);

	system("pause");
	return 0;
}

 

3.3 思考小结

        因为有两个指针,头指针和尾指针,便于直接在队头执行删除,队尾执行插入操作,而不用像单链表那样需要遍历一次,更加方便,只是插入删除的位置受限而已。

四、双端队列

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值