Bootstrap

C++队列

前言

队列的存储结构有链式和数组两种形式,链式队列较为简单,可以直接通过链表获得,而用数组实现队列则有很多细节,后文中会给出来,本文默认大家已经了解队列的先入先出的特点,重点在于代码实现,如果读者对队列还不熟悉建议先看看书或者视频再来看本文。


数组队列

类的定义

由于我们队列的两种形式都要实现,而两种队列的基本操作其实都是一样的,只不过是函数的实现不同,所以我们定义了抽象类

template<class Type>
 class queueADT
 {
 public:
	 virtual bool isEmpty()const = 0;
	 virtual bool isFull()const = 0;
	 virtual void initializeQueue() = 0;
	 virtual Type front()const = 0;
	 virtual Type back()const = 0;
	 virtual void addElem(const Type&) = 0;
	 virtual void deleteElem() = 0;
	 virtual int queueLen()const = 0;
 };

然后通过类的继承来构造数组队列类,我们将数组的最大容量作为成员变量方便初始化。

template<class Type>
class queueArr :public queueADT<Type>
{
public:
	const queueArr<Type>& operator=(const queueArr<Type>&);
	bool isEmpty()const;
	bool isFull()const;
	void initializeQueue();
	Type front()const;
	Type back()const;
	int queueLen()const;
	void addElem(const Type&);
	void deleteElem();
	queueArr(int size = 100);  //设置默认最大数组长度
	queueArr(const queueArr<Type>&);
	~queueArr();
	void printQueue()const;
private:
	int maxSize;
	int head;
	int tail;
	Type* headPtr;
};

成员函数的实现

  • isEmpty:判断队列是否为空
template<class Type>
bool queueArr<Type>::isEmpty()const
{
	return tail == head;
}

注意:我们这里采用的区别队列满和空的手段是在数组中空一个位置,和王卓老师视频里用的方法一样,实际上比较简单的是将当前的元素数目作为成员变量,tail指针指向的是空置的位置。

  • isFull:判断队列是否已满
template<class Type>
bool queueArr<Type>::isFull()const
{
	return (tail+1)%maxSize == head; 
}
  • initializeQueue:初始化队列,这里的操作就是将头指针和尾指针都指向第一个元素
template<class Type>
void queueArr<Type>::initializeQueue()
{
	head = 0;
	tail = 0;
}
  • front:获得队首元素
template<class Type>
Type queueArr<Type>::front()const
{
	assert(!isEmpty());  //防止队列为空返回错误值
	return headPtr[head];
}
  • back:返回队尾元素
template<class Type>
Type queueArr<Type>::back()const
{
	assert(!isEmpty());
	return headPtr[(tail-1+maxSize)%maxSize]; 
	//注意tail如果为0那么减去1就会是负数,因此要加上数组容量,同时为了防止加了之后的
	//索引值超过了数组最大容量,所以要对maxSize取余
}
  • queueLen:返回当前队列长度
template<class Type>
int queueArr<Type>::queueLen()const
{
	return (tail - head + maxSize) % maxSize;
}

注意:在数组队列中凡是涉及到指针的加减,通通要取余数,如果经过加减后可能出现负数,那么在取余前还要加上数组最大长度。循环计数的精髓就是要取余。

  • addElem:向队列中添加元素(添加到队尾)
template<class Type>
void queueArr<Type>::addElem(const Type& elem)
{
	if (!isFull())
	{
		headPtr[tail] = elem;
		tail = (tail + 1) % maxSize;
	}
	else
		cout << "The queue is full,can not add an element in it" << endl;
}
  • deleteElem:删除队首元素
template<class Type>
void queueArr<Type>::deleteElem()
{
	if (!isEmpty())
		head = (head + 1) % maxSize;
	else
		cout << "can not remove an element from the queue" << endl;
}
  • 构造函数
template<class Type>
queueArr<Type>::queueArr(int size)
{
	if (size <= 0)
	{
		cout << "Size of the array to hold the queue must "
			<< "be positive." << endl;
		cout << "Creating an array of size 100." << endl;
		maxSize = 100;
		headPtr = new Type[maxSize];
		assert(headPtr != nullptr);
	}
	else
	{
		maxSize = size;
		head = 0;
		tail = 0;
		headPtr = new Type[maxSize];
		assert(headPtr != nullptr); //记得判断内存是否分配成功
	}
}
  • 拷贝构造
template<class Type>
queueArr<Type>::queueArr(const queueArr<Type>& otherQueue)
{
	head = otherQueue.head;
	tail = otherQueue.tail;
	maxSize = otherQueue.maxSize;
	headPtr = new Type[maxSize];
	assert(headPtr != nullptr);
	for (int i = 0; i < otherQueue.queueLen(); i++)
		headPtr[(head + i) % maxSize] = otherQueue.headPtr[(head + i) % maxSize];//特别注意,拷贝必须从队头开始拷贝(即从head处开始),一直拷贝到队尾,拷贝长度就是队列的长度
}
  • 析构函数
template<class Type>
queueArr<Type>::~queueArr()
{
	delete[]headPtr;
}
  • =运算符重载
template<class Type>
const queueArr<Type>& queueArr<Type>::operator=(const queueArr<Type>& otherQueue)
{
	if (this != &otherQueue)
	{
		head = otherQueue.head;
		tail = otherQueue.tail;
		if (maxSize != otherQueue.maxSize)
		{
			delete[]headPtr;
			headPtr = new Type[otherQueue.maxSize];
			assert(headPtr != nullptr);
		}
		maxSize = otherQueue.maxSize;
		if (!otherQueue.isEmpty())
		{
			for (int i = 0; i < otherQueue.queueLen(); i++)
				headPtr[(head+i)%maxSize] = otherQueue.headPtr[(head + i) % maxSize];  //神来之笔,不然因为空了一个元素,而且元素位置不确定,一旦赋值就会出现问题
		}
	}
	else
	{
		cout << "cannot self assignment" << endl;
	}
	return *this;
}
  • printQueue:打印队列的信息
template<class Type>
void queueArr<Type>::printQueue()const
{
	if (!isEmpty())
	{
		cout << "The queue:";
		for (int i = 0; i < queueLen(); i++)
		{
			cout << headPtr[(head + i) % maxSize] << " ";
		}
		cout << endl;
	}
}

全部代码

链式队列很多操作和链表类似,也可以直接从链表获得,这里就不详细介绍了,不过还是给出代码以供参考。

queueADT.h

#pragma once
#include<iostream>
using namespace std;
 template<class Type>
 class queueADT
 {
 public:
	 virtual bool isEmpty()const = 0;
	 virtual bool isFull()const = 0;
	 virtual void initializeQueue() = 0;
	 virtual Type front()const = 0;
	 virtual Type back()const = 0;
	 virtual void addElem(const Type&) = 0;
	 virtual void deleteElem() = 0;
	 virtual int queueLen()const = 0;
 };

queueArr.h

#pragma once
#include<iostream>
#include<assert.h>
#include "queueADT.h"
template<class Type>
class queueArr :public queueADT<Type>
{
public:
	const queueArr<Type>& operator=(const queueArr<Type>&);
	bool isEmpty()const;
	bool isFull()const;
	void initializeQueue();
	Type front()const;
	Type back()const;
	int queueLen()const;
	void addElem(const Type&);
	void deleteElem();
	queueArr(int size = 100);
	queueArr(const queueArr<Type>&);
	~queueArr();
	void printQueue()const;
private:
	int maxSize;
	int head;
	int tail;
	Type* headPtr;
};

template<class Type>
bool queueArr<Type>::isEmpty()const
{
	return tail == head;
}

template<class Type>
bool queueArr<Type>::isFull()const
{
	return (tail+1)%maxSize == head;
}

template<class Type>
void queueArr<Type>::initializeQueue()
{
	head = 0;
	tail = 0;
}

template<class Type>
Type queueArr<Type>::front()const
{
	assert(!isEmpty());
	return headPtr[head];
}

template<class Type>
Type queueArr<Type>::back()const
{
	assert(!isEmpty());
	return headPtr[(tail-1+maxSize)%maxSize];
}

template<class Type>
int queueArr<Type>::queueLen()const
{
	return (tail - head + maxSize) % maxSize;
}

template<class Type>
void queueArr<Type>::addElem(const Type& elem)
{
	if (!isFull())
	{
		headPtr[tail] = elem;
		tail = (tail + 1) % maxSize;
	}
	else
		cout << "The queue is full,can not add an element in it" << endl;
}

template<class Type>
void queueArr<Type>::deleteElem()
{
	if (!isEmpty())
		head = (head + 1) % maxSize;
	else
		cout << "can not remove an element from the queue" << endl;
}

template<class Type>
queueArr<Type>::queueArr(int size)
{
	if (size <= 0)
	{
		cout << "Size of the array to hold the queue must "
			<< "be positive." << endl;
		cout << "Creating an array of size 100." << endl;
		maxSize = 100;
		headPtr = new Type[maxSize];
		assert(headPtr != nullptr);
	}
	else
	{
		maxSize = size;
		head = 0;
		tail = 0;
		headPtr = new Type[maxSize];
		assert(headPtr != nullptr); //记得判断内存是否分配成功
	}
}

template<class Type>
queueArr<Type>::queueArr(const queueArr<Type>& otherQueue)
{
	head = otherQueue.head;
	tail = otherQueue.tail;
	maxSize = otherQueue.maxSize;
	headPtr = new Type[maxSize];
	assert(headPtr != nullptr);
	for (int i = 0; i < otherQueue.queueLen(); i++)
		headPtr[(head + i) % maxSize] = otherQueue.headPtr[(head + i) % maxSize];
}

template<class Type>
queueArr<Type>::~queueArr()
{
	delete[]headPtr;
}

template<class Type>
const queueArr<Type>& queueArr<Type>::operator=(const queueArr<Type>& otherQueue)
{
	if (this != &otherQueue)
	{
		head = otherQueue.head;
		tail = otherQueue.tail;
		if (maxSize != otherQueue.maxSize)
		{
			delete[]headPtr;
			headPtr = new Type[otherQueue.maxSize];
			assert(headPtr != nullptr);
		}
		maxSize = otherQueue.maxSize;
		if (!otherQueue.isEmpty())
		{
			for (int i = 0; i < otherQueue.queueLen(); i++)
				headPtr[(head+i)%maxSize] = otherQueue.headPtr[(head + i) % maxSize];  //神来之笔,不然因为空了一个元素,而且元素位置不确定,一旦赋值就会出现问题
		}
	}
	else
	{
		cout << "cannot self assignment" << endl;
	}
	return *this;
}

template<class Type>
void queueArr<Type>::printQueue()const
{
	if (!isEmpty())
	{
		cout << "The queue:";
		for (int i = 0; i < queueLen(); i++)
		{
			cout << headPtr[(head + i) % maxSize] << " ";
		}
		cout << endl;
	}
}

queueLink.h

#pragma once
#include<iostream>
#include<assert.h>
#include "queueADT.h"
using namespace std;
template<class Type>
struct node
{
	Type elem;
	node<Type>* next;
};

template<class Type>
class queueLink:public queueADT<Type>
{
public:
	 bool isEmpty()const ;
	 bool isFull()const ;
	 void initializeQueue() ;
	 Type front()const ;
	 Type back()const ;
	 void addElem(const Type&) ;
	 void deleteElem() ;
	 int queueLen()const ;
	 queueLink();
	 queueLink(const queueLink<Type>&);
	 ~queueLink();
	 const queueLink<Type>& operator=(const queueLink<Type>&);
private:
	node<Type>* head;
	node<Type>* tail;
	int length;
	void copyQueue(const queueLink<Type>&);
};

template<class Type>
bool queueLink<Type> ::isEmpty()const
{
	return head == nullptr;
}

template<class Type>
bool queueLink<Type> ::isFull()const
{
	return false;
}

template<class Type>
void queueLink<Type>::initializeQueue()
{
	node<Type>* temp = head;
	while (head)
	{
		head = head->next;
		delete temp;
		temp = head;
	}
	tail = nullptr;
	length = 0;
}
template<class Type>
Type queueLink<Type>::front()const
{
	assert(!isEmpty());
	return head->elem;
}
template<class Type>
Type queueLink<Type>::back()const
{
	assert(!isEmpty());
	return tail->elem;
}
template<class Type>
void queueLink<Type>::addElem(const Type& elem)
{
	node<Type>* newNode = new node<Type>;
	newNode->elem = elem;
	newNode->next = nullptr;
	if (head == nullptr)
	{
		tail = newNode;
		head = newNode;
	}
	else
	{
		tail->next = newNode;
		tail = tail->next;
	}
	length++;
}
template<class Type>
void queueLink<Type>::deleteElem()
{
	if (!isEmpty())
	{
		node<Type>* temp = head;
		head = head->next;
		delete temp;
		length--;
	}
	else
		cout << "The queue is empty,cannot remove an element" << endl;
}
template<class Type>
int queueLink<Type>::queueLen()const
{
	return length;
}
template<class Type>
queueLink<Type>::queueLink()
{
	head = nullptr;
	tail = head;
	length = 0;
}
template<class Type>
queueLink<Type>::~queueLink()
{
	initializeQueue();
}
template<class Type>
void queueLink<Type>::copyQueue(const queueLink<Type>& otherQueue)
{
	node<Type>* temp,otherCurrent;
	otherCurrent = otherQueue.head;
	if (head)
		initializeQueue();
	if (otherQueue.head)
	{
		head = new node<Type>;
		tail = head;
		head->elem = otherCurrent->elem;
		head->next = otherCurrent->next;
		otherCurrent = otherCurrent->next;
		length++;
		while (otherCurrent)
		{
			temp = new node<Type>;
			temp->next = otherCurrent->next;
			temp->elem = otherCurrent->elem;
			tail->next = temp;
			tail = tail->next;
			otherCurrent = otherCurrent->next;
			length++;
		}
	}
}
template<class Type>
queueLink<Type>::queueLink(const queueLink<Type>& otherQueue)
{
	copyQueue(otherQueue);
}
template<class Type>
const queueLink<Type>& queueLink<Type>::operator=(const queueLink<Type>& otherQueue)
{
	if (this != &otherQueue)
	{
		copyQueue(otherQueue);
	}
	return *this;
}

test.cpp

#include<iostream>
#include "queueArr.h"
#include "queueLink.h"
using namespace std;

int main()
{
	queueArr<int> que1,que2(4);
	que1.addElem(11);
	que1.addElem(12);
	que1.addElem(13);
	que1.addElem(13);
	que1.addElem(13);
	que1.printQueue();
	cout << que1.queueLen() << endl;
	que1.deleteElem();
	que1.addElem(44);
	que1.deleteElem();
	que1.addElem(32);
	que2 = que1;
	que1.printQueue();
	que2.printQueue();
	queueArr<int> que3(que1);
	que3.printQueue();
	que1.printQueue();
	cout << que1.queueLen() << endl;
	que2 = que1;
	que2.printQueue();
	que2.deleteElem();
	que2.deleteElem();
	que2.deleteElem();
	cout << que1.front() << "," << que1.back() << endl;

	queueLink<int> queL1,queL2;
	queL1.addElem(13);
	queL1.addElem(76);
	
	cout << queL1.front()<<" " << queL1.back();
	return 0;
}
;