数据结构笔记四_队列(C++)

本文详细介绍了队列的逻辑结构,包括队列的概念、基本操作和抽象类定义。接着讨论了队列的两种物理结构——顺序结构(循环队列)和链式结构,分析了它们的特点和实现方法。对于顺序结构,重点讲解了循环队列的存储方式和空间扩展策略。链式结构则介绍了单链表实现的队列。最后,对比了两种实现的优缺点,并给出了应用代码示例。
摘要由CSDN通过智能技术生成

队列

一、队列的逻辑结构

1. 队列的概念

越早到达越早离开,先进先出
例如:银行储蓄柜前排队,计算机打印管理器中对打印队列的管理。
在这里插入图片描述

2. 队列的基本操作

结合生活实际,队列的基本操作如下:

构造类
  • 创建队列create():创建一个空的队列
属性类
  • 判断队空isEmpty(): 空,返回true
  • 读队首元素getHead(): 返回队首元素的值
操纵类
  • 入队enQueue(x): 将x插入队尾,使之成为队尾元素
  • 出队deQueue(): 删除队首元素并返回队首元素值

3. 队列类的抽象类

template<class elemType>
class queue {
public:
	virtual bool isEmpty() = 0;
	virtual elemType getHead() = 0;
	virtual void enQueue(cosnt elemType& x) = 0;
	virtual elemType  deQueue() = 0;
	virtual ~queue() {};
};

二、队列的物理结构

1. 队列的顺序结构

(1) 顺序存储的三种方式介绍
  • 对头位置固定
    a. 对头固定在下标0
    b. 用一个变量指向队尾位置
    c. 队列为空时,队尾位置为-1
    在这里插入图片描述
    缺点: 出队时会引起大量数据的向前移动,时间复杂度较高
  • 队头位置不固定
    即出队时,队首指针front(指示队首节点的前一位置下标)向后移动
    a. 队列初始化时,设front=rear=-1,即队空的标志:front=rear
    b. 队满的标志:rear=Maxsize-1

在这里插入图片描述
缺点: 时间复杂度为 O ( 1 ) O(1) O(1),但出队时会空出前面的位置,造成空间的浪费

  • 循环队列
    如何将上一种情况浪费的空间利用起来,即是循环队列所考虑并解决的问题。
    我们可以从逻辑上认为单元0就是Maxsize,但rear已经指向最后但是前面还有位置时,便可以“从头入队”。
    在这里插入图片描述
    如何实现这个“转弯”操作呢?
    r e a r = ( r e a r + 1 ) % M a x s i z e rear=(rear+1)\%Maxsize rear=(rear+1)%Maxsize利用上面的公式便可以实现从数组尾跳到数组头的操作
    下面考虑具体操作:
    a. 入队操作: r e a r = ( r e a r + 1 ) % M a x s i z e rear=(rear+1)\%Maxsize rear=(rear+1)%Maxsize
    b. 出队操作: f r o n t = ( f r o n t + 1 ) % M a x s i z e front=(front+1)\%Maxsize front=(front+1)%Maxsize
    c. 判断队满和队空:
    队空: 当队列中只有一个元素时,此时rear和front相邻,rear在front后面。执行出队操作后,front与rear相同,因此队列为空的标志为: f r o n t = = r e a r front==rear front==rear
    队满: 当数组中只剩下一个空单元,就是front所指向的单元,执行入队操作后,front与rear相同,!!!和队空时情况相同。
    解决方案: “牺牲”一个单元的空间,规定front所指向的单元不能存储元素,只起到标志作用,表示后面一个是队头元素,当还剩一个空单元时表示队列已满。即队满的标志为: ( r e a r + 1 ) % M a x s i z e = = f r o n t (rear+1)\%Maxsize==front (rear+1)%Maxsize==front
(2)循环队列类的声明

根据上述说明,给出循环队列类的声明如下

template<class elemType>
class seqQueue :public queue<elemType> {
private:
	elemType* elem;
	int maxSize;
	int front, rear;
	void doublespace();
public:
	seqQueue(int size = 10) {
		elem = new elemType[size];
		maxSize = size;
		front = rear = 0;
	}
	~seqQueue() { delete[]elem; };
	bool isEmpty() {
		return front == rear;
	}
	elemType getHead() {
		return elem[(front + 1) % maxSize];
	}
	void enQueue(const elemType& x);
	elemType deQueue();
};

2. 队列的链式结构

(1)队列的链式存储

由于队列的操作是在队列的两端进行的,不会对队列中的其他元素进行操作,用不带头结点的单链表即可。
其中front指向队首,rear指向队尾。
在这里插入图片描述
存储特性分析:

  • 链式存储并不会出现队满的情况,但需考虑队空的情况
  • 队列为空时,单链表中没有节点存在,即收尾均为空指针
  • 出入队均为 O ( 1 ) O(1) O(1)的时间复杂度
(2)链式队列的声明
template<class elemType>
class linkQueue :public queue<elemType> {
private:
	struct node {
		elemType data;
		node* next;
		node(const elemType& x, node* N = NULL) {
			data = x;
			next = N;
		}
		node() :next(NULL) {}
		~node(){}
	};
	node* front, *rear;
public:
	linkQueue() {
		front = rear = NULL;
	}
	~linkQueue();
	bool isEmpty() {
		return front == NULL;
	}
	elemType getHead() {
		return front->data;
	}
	void enQueue(const elemType& x);
	elemType deQueue();
};

三、队列的实现

1. 顺序循环队列实现

  • doublespace()
    和线性表的doublespace不同,新建扩大后的数组后将front后的首个元素“搬移”到新数组的1下标位置
template<class elemType>
void seqQueue<elemType>::doublespace() {
	elemType* tmp = elem;
	elem = new elemType[2 * maxSize];
	for (int i = 1; i < maxSize; ++i) {
		elem[i] = tmp[(front + i) % maxSize];
	}
	front = 0;
	rear = maxSize - 1;
	maxSize *= 2;
	delete tmp;
}
  • enQueue() 进队操作
template<class elemType>
void seqQueue<elemType>::enQueue(const elemType& x) {
	//如果数组已经满了,则空间加倍
	if ((rear + 1) % maxSize == front) doublespace();
	rear = (rear + 1) % maxSize;
	elem[rear] = x;
}

  • deQueue() 出队操作
template<class elemType>
elemType seqQueue<elemType>::deQueue() {
	front = (front + 1) % maxSize;
	return elem[front];
}

2.链式队列的实现

  • enQueue()进队操作
template<class elemType>
void linkQueue<elemType>::enQueue(const elemType& x) {
	if (front == NULL) {
		front = rear = new node(x);
	}
	else {
		rear->next = new node(x);
		rear = rear->next;
	}
}
  • deQueue()出队操作
template<class elemType>
elemType linkQueue<elemType>::deQueue() {
	node* tmp = front;
	elemType value = tmp->data;
	front = front->next;
	if (front == NULL) rear = NULL;
	delete tmp;
	return value;
}
  • ~linkQueue()析构操作
    一定要注意链式结构的析构(利用兄弟协同法一直删首节点)
template<class elemType>
linkQueue<elemType>::~linkQueue() {
	node* tmp;
	while (front != NULL) {
		tmp = front;
		front = front->next;
		delete tmp;
	}
}

3. 顺序实现和链式实现的比较

  • 时间: 两者都能在常量的时间内完成基本操作,但顺序队列由于采用回绕,使出队和入队处理较为麻烦
  • 空间: 链接队列中,每个节点多一个指针字段,但在顺序队列中有大量尚未使用的空间

四、队列的应用

代码总结和测试

  1. 顺序实现
  • seqQueue.h
# include<iostream>
using namespace std;

template<class elemType>
class queue {
public:
	virtual bool isEmpty() = 0;
	virtual elemType getHead() = 0;
	virtual void enQueue(const elemType& x) = 0;
	virtual elemType  deQueue() = 0;
	virtual ~queue() {};
};

template<class elemType>
class seqQueue :public queue<elemType> {
private:
	elemType* elem;
	int maxSize;
	int front, rear;
	void doublespace();
public:
	seqQueue(int size = 10) {
		elem = new elemType[size];
		maxSize = size;
		front = rear = 0;
	}
	~seqQueue() { delete[]elem; }
	bool isEmpty() {
		return front == rear;
	}
	elemType getHead() {
		return elem[(front + 1) % maxSize];
	}
	void enQueue(const elemType& x);
	elemType deQueue();
};

template<class elemType>
void seqQueue<elemType>::doublespace() {
	elemType* tmp = elem;
	elem = new elemType[2 * maxSize];
	for (int i = 1; i < maxSize; ++i) {
		elem[i] = tmp[(front + i) % maxSize];
	}
	front = 0;
	rear = maxSize - 1;
	maxSize *= 2;
	delete tmp;
}

template<class elemType>
void seqQueue<elemType>::enQueue(const elemType& x) {
	//如果数组已经满了,则空间加倍
	if ((rear + 1) % maxSize == front) doublespace();
	rear = (rear + 1) % maxSize;
	elem[rear] = x;
}

template<class elemType>
elemType seqQueue<elemType>::deQueue() {
	front = (front + 1) % maxSize;
	return elem[front];
}
  • seqQueue.cpp(测试代码)
#include<iostream>
#include"seqQueue.h"
using namespace std;

int main() {
	seqQueue<int> s(10);
	for (int i = 0; i < 15; ++i)
		s.enQueue(i);
	while (!s.isEmpty())
		cout << s.deQueue() << " ";
	cout << endl;
	return 0;
}
  1. 链式实现
    linkQueue.h
#include<iostream>
using namespace std;

template<class elemType>
class queue {
public:
	virtual bool isEmpty() = 0;
	virtual elemType getHead() = 0;
	virtual void enQueue(const elemType& x) = 0;
	virtual elemType  deQueue() = 0;
	virtual ~queue() {};
};

template<class elemType>
class linkQueue :public queue<elemType> {
private:
	struct node {
		elemType data;
		node* next;
		node(const elemType& x, node* N = NULL) {
			data = x;
			next = N;
		}
		node() :next(NULL) {}
		~node(){}
	};
	node* front, *rear;
public:
	linkQueue() {
		front = rear = NULL;
	}
	~linkQueue();
	bool isEmpty() {
		return front == NULL;
	}
	elemType getHead() {
		return front->data;
	}
	void enQueue(const elemType& x);
	elemType deQueue();
};

template<class elemType>
void linkQueue<elemType>::enQueue(const elemType& x) {
	if (front == NULL) {
		front = rear = new node(x);
	}
	else {
		rear->next = new node(x);
		rear = rear->next;
	}
}

template<class elemType>
elemType linkQueue<elemType>::deQueue() {
	node* tmp = front;
	elemType value = tmp->data;
	front = front->next;
	if (front == NULL) rear = NULL;
	delete tmp;
	return value;
}

template<class elemType>
linkQueue<elemType>::~linkQueue() {
	node* tmp;
	while (front != NULL) {
		tmp = front;
		front = front->next;
		delete tmp;
	}
}
  • linkQueue.cpp(测试程序)
#include<iostream>
#include"linkQueue.h"
using namespace std;

int main() {
	linkQueue<int> s;
	for (int i = 0; i < 15; ++i)
		s.enQueue(i);
	while (!s.isEmpty())
		cout << s.deQueue() << " ";
	cout << endl;
	return 0;
}
  1. 运行结果
    请添加图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值