线性表--队列的数组实现
程序员文章站
2022-07-14 14:02:10
...
定义和应用
队列(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;
}
}