线性表--队列的数组实现

定义和应用

队列(queue)是一个线性表,其插入和删除分别在表的不同端进行。它是一种先进先出(FIFO)的线性表,插入元素的一端称为队尾(bakc或者rear),删除元素的一段叫作队首(front)。

以数组来实现,需要考虑空间的利用率。如果采用类似数组和栈一般的映射公式location(i)=i,那么每次删除数组前端(队首)元素需要将数组整体左移一位,显然效率低下。考虑新的映射公式location(i)=location(队首元素)+i,能很好地解决这一问题,但同时也有了一个新的问题,那就是在一系列的插入操作后,数组前端(队首位置)存在大量未使用的空地址,因此空间利用率低,如果将数组中所有元素整体左移,那么和刚才的方法一样,时间复杂度又为渐近大于arrayLength。

因此我们考虑将数组视为环,来实现环形的队列。环形数组实现的队列的映射公式为 location(i)=(location(队首元素)+i)%arrayLength 。环形队列也有一个问题,那就是queueFront==queueBack时,到底是表示队空还是队满呢?因此我们改变队首元素指针queueFront的约定,让他指向队首元素的前一个位置,这样当且仅当queueFront==queueBack时才表示队空,而如果 (queueBack+1)%arrayLength==queueFront时,就需要加倍数组并复制原有元素了。因此,该实现中,队列的元素个数最多是arrayLength-1个。

具体实现

queueADT.h

#pragma once
template <typename T>
class queueADT {
public:
	virtual ~queueADT(){ }
	virtual bool empty()const = 0;
	virtual size_t size()const = 0;
	virtual T& front()const = 0;//返回队首元素的引用
	virtual T& back()const = 0;//返回队尾元素的引用
	virtual void pop() = 0;//在队首删除元素
	virtual void push(const T& _element) = 0;//在队尾插入元素
};

queueEmpty.h

#pragma once
#include <string>
using std::string;
#include <iostream>
using std::cin; using std::cout; using std::cerr; using std::ostream; using std::istream;
using std::ends; using std::endl;
class queueEmpty {
public:
	queueEmpty(string _message = "Invalid operation on empty queue!") :message(_message) { }
	~queueEmpty() { }
	string what()const { return message; }
	void output()const { cerr << message << endl; }
private:
	string message;
};

queueArray.h

#pragma once
#ifndef queueArray_H
#define queueArray_H
#include "queueADT.h"
#include "queueEmpty.h"

template <typename T>
class queueArray :public queueADT<T> {
public:
	queueArray(size_t _capacity=10):
		queue(new T[_capacity]),capacity(_capacity),queueFront(0),queueBack(0){ }
	~queueArray() { delete[] queue; }

	bool empty()const { return queueFront == queueBack; }
	size_t size()const { return (queueBack - queueFront + capacity) % capacity; }
	T& front()const;
	T& back()const;
	void pop();
	void push(const T& _element);
	void output();

private:
	T* queue;		//元素数组
	size_t capacity;	//队列的容量capacity
	size_t queueFront;	//队首元素的前一个位置的下标
	size_t queueBack;	//队尾元素下标
};

template <typename T>
inline T& queueArray<T>::front()const {
	if (queueFront == queueBack)
		throw queueEmpty("Can not get front element on empty queue!");
	return queue[(queueFront + 1) % capacity];
}

template <typename T>
inline T& queueArray<T>::back()const {
	if (queueFront == queueBack)
		throw queueEmpty("Can not get back element on empty queue!");
	return queue[queueBack];
}

template <typename T>
inline void queueArray<T>::pop() {
	if (queueFront == queueBack)
		throw queueEmpty("Can not pop on empty queue!");
	queueFront = (++queueFront) % capacity;
	queue[queueFront].~T();
}

template <typename T>
void queueArray<T>::push(const T& _element) {
	//因为当且仅当queueFront==queueuBack的时候,队列为空。如果允许容器装满元素,那么这时候
	//也存在queueFront==queueuBack的情况。所以我们不能允许容器盛满元素。
	if ((queueBack+1)%capacity==queueFront) {//需要翻倍
		T* newQueue = new T[2 * capacity];
		//判断原数组中元素是否分段了 即Front指向的空元素的空间是否将其他数据隔开了
		size_t start = (queueFront + 1) % capacity;
		//因为首元素的下标也是(queueFront + 1) % capacity,因此也可以理解为:首元素是否在queue[0]或者queue[1]
		//上,如果不在的话则说明数据被首元素的前一个位置的queue[Front]给隔开了,形成了环
		if (start < 2) {//没有隔开 未形成环
			//直接把连续的数据拷贝到新数组中
			std::copy(queue+start,queue+start+capacity-1,newQueue);//注std::copy()拷贝范围是[first,last)
		}
		else {//被隔开了 队列形成了换
			std::copy(queue+start,queue+capacity-1,newQueue);//拷贝原数组前半段数据
			std::copy(queue,queue+queueFront,newQueue+capacity-start);//拷贝原数组后半段数据
		}
		//更新数据成员
		queueBack = capacity - 2;
		capacity *= 2;
		queueFront = capacity - 1;
		delete[] queue;
		queue = newQueue;
	}
	queueBack = (++queueBack) % capacity;
	queue[queueBack] = _element;
}

template <typename T>
void queueArray<T>::output() {
	size_t iter = 0;
	size_t pos = queueFront;
	while (iter<size()) {
		pos = (++pos) % capacity;
		cout << queue[pos] << " ";
		++iter;
	}
	cout << endl;
}
#endif

测试代码

#include "queueArray.h"
#include "..//..//..//ch06/ChainList/ChainList/illegalParameterValue.h"
#include "..//..//..//ch08/Stack/Stack/stackArray.h"

//===================================test for queueArray===================================
int main(){
	queueArray<double> q(5);
	if(q.empty())
		cout << "first,q.size()=" << q.size() << endl;
	q.push(1);
	cout << "rear of queue=" << q.back() << endl;
	q.push(2);
	cout << "rear of queue=" << q.back() << endl;
	q.push(3);
	cout << "rear of queue=" << q.back() << endl;
	q.push(4);
	cout << "rear of queue=" << q.back() << endl;
	q.push(5);
	cout << "rear of queue=" << q.back() << endl;
	q.output();
	cout << "after pop,q.size()=" << q.size() << endl;
	while (!q.empty()){
		cout << "pop the front element:" << q.front() << endl;
		q.pop();
	}
	try {
		q.pop();
	}
	catch(const queueEmpty& e){
		cout << "now q.size()=" << q.size() << endl;
		cout << e.what() << endl;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值