数据结构实现 1.3:数组队列(C++版)
1. 概念及基本框架
队列 可以看做一种特殊的 数组 ,所以我使用第一节实现的 动态数组 来实现队列这种数据结构。当然,队列也可以通过其他方式来实现。因为该队列是通过动态数组实现的,所以称之为 数组队列 。
队列的结构如上图所示,可知队列的基本特性如下:
1.队列 有 队头 和 队尾 两端。
2.入队 操作只能从 队尾 进行,出队 操作只能从 队头 进行。
3.先 入队 的先 出队 ,即 先进先出(First In First Out),FIFO 。
还有一个隐含特性,队列可以自行 扩容(缩容),而不需要用户关心,很显然,动态数组已经满足了这个要求。
由此可见,队列的操作并不多,我使用了一个由 纯虚函数 构成的 抽象类 作为一个接口来定义这些操作。具体代码如下:
template <class T>
class Queue{
public:
virtual int size() = 0;
virtual bool isEmpty() = 0;
virtual void print() = 0;
//入队操作
virtual void enqueue(T num) = 0;
//出队操作
virtual void dequeue() = 0;
//获得队首元素
virtual T front() = 0;
};
下面只需要通过继承 抽象类,并且重写 纯虚函数 ,就可以完成 数组队列 的实现。数组队列类的框架如下:
template <class T>
class ArrayQueue : public Queue<T>{
public:
ArrayQueue(int len = initialLen){
Array<T> *p = new Array<T>(len);
arr = p;
}
...
private:
Array<T> *arr;
};
这个类内部定义一个动态数组类对象,为了保护数据,把它放在 private 部分,构造函数 ArrayQueue 底层调用的就是 Array 的构造函数。用户在构造函数中也可以指定队列的大小。(默认是10)为了兼容更多类型,这里使用了泛型的概念。
2. 基本操作程序实现
2.1 入队操作
template <class T>
class ArrayQueue : public Queue<T>{
public:
...
//入队操作
void enqueue(T num){
arr->addLast(num);
}
...
};
入队操作使用了动态数组的增加第一个元素的操作来实现。这里动态数组内部已经提供了扩容操作。
2.2 出队操作
template <class T>
class ArrayQueue : public Queue<T>{
public:
...
//出队操作
void dequeue(){
arr->removeFirst();
}
...
};
出队操作使用了动态数组的删除第一个元素的操作来实现。这里动态数组内部已经提供了缩容操作。
2.3 查找操作
template <class T>
class ArrayQueue : public Queue<T>{
public:
...
//获得队首元素
T front(){
return arr->get(0);
}
...
};
因为队列只能获得队首元素,所以这里的查找操作也非常简单。
2.4 其他操作
template <class T>
class ArrayQueue : public Queue<T>{
public:
...
int size(){
return arr->size();
}
bool isEmpty(){
return arr->size() == 0;
}
void print(){
cout << "ArrayQueue: ";
cout << "Size = " << arr->size() << endl;
cout << "front[";
for (int i = 0; i < arr->size(); ++i){
cout << arr->get(i);
if (i != arr->size() - 1){
cout << ',';
}
}
cout << "]rear" << endl;
}
...
};
3. 算法复杂度分析
3.1 入队操作
函数 | 最坏复杂度 | 平均复杂度 |
---|---|---|
enqueue | O(1+n) = O(n) | O(1+1) = O(1) |
最坏复杂度 O(1+n) 中第一个 1 是指元素移动操作,第二个 n 是指动态数组中的 resize 函数,以下同理。
入队可能会引发扩容操作,平均而言,每增加 n 个元素,会扩展一次,会发生 n 个元素的移动,所以平均下来是 O(1) 。
3.2 出队操作
函数 | 最坏复杂度 | 平均复杂度 |
---|---|---|
dequeue | O(n+n) = O(n) | O(n+1) = O(n) |
3.3 查找操作
函数 | 最坏复杂度 | 平均复杂度 |
---|---|---|
front | O(1) | O(1) |
总体情况:
操作 | 时间复杂度 |
---|---|
增 | O(1) |
删 | O(n) |
查 | O(1) |
由此可以看出,队列操作的增、查都是 O(1) 级别的时间复杂度,而删是 O(n) 级别的时间复杂度,因为每次出队的都是队首元素,后面的元素需要一个个向前移动。
注:队列并不提供改的操作。
4. 完整代码
动态数组类 代码:
#ifndef __ARRAY_H__
#define __ARRAY_H__
using namespace std;
const int initialLen = 10;
template <class T>
class Array{
public:
Array(int len = initialLen){
T *p = new T[len];
m_data = p;
m_capacity = len;
m_size = 0;
}
int capacity(){
return m_capacity;
}
int size(){
return m_size;
}
void resize(int len){
T *p = new T[len];
for (int i = 0; i < m_size; ++i){
p[i] = m_data[i];
}
delete[] m_data;
m_data = p;
m_capacity = len;
}
bool isEmpty(){
return m_size == 0;
}
void print(){
cout << "Array: ";
cout << "Capacity = " << m_capacity << ", " << "Size = " << m_size << endl;
cout << '[';
for (int i = 0; i < m_size; ++i){
cout << m_data[i];
if (i != m_size - 1){
cout << ',';
}
}
cout << ']' << endl;
}
//增加操作
void add(int index, T num);
void addFirst(T num);
void addLast(T num);
//删除操作
T remove(int index);
T removeFirst();
T removeLast();
void removeElement(T num);
//修改操作
void set(int index, T num);
//查找操作
T get(int index);
int find(T num);
bool contains(T num);
private:
T *m_data; //数组数据
int m_capacity; //数组容量
int m_size; //数组大小
};
template <typename T>
void Array<T>::add(int index, T num){
if (index < 0 || index > m_size){
cout << "添加位置非法!" << endl;
return;
//throw 0; //这里可以使用抛异常,下面也可以
}
if (m_size >= m_capacity){
resize(2 * m_capacity);
}
for (int i = m_size; i > index; --i){
m_data[i] = m_data[i - 1];
}
m_data[index] = num;
m_size++;
}
template <class T>
void Array<T>::addFirst(T num){
add(0, num);
}
template <class T>
void Array<T>::addLast(T num){
if (m_size >= m_capacity){
resize(2 * m_capacity);
}
m_data[m_size] = num;
m_size++;
}
template <class T>
T Array<T>::remove(int index){
if (index < 0 || index >= m_size){
cout << "删除位置非法!" << endl;
return NULL;
}
T res = m_data[index];
m_size--;
for (int i = index; i < m_size; ++i){
m_data[i] = m_data[i + 1];
}
if (m_size < m_capacity / 4){
resize(m_capacity / 2);
}
return res;
}
template <class T>
T Array<T>::removeFirst(){
T res = m_data[0];
remove(0);
return res;
}
template <class T>
T Array<T>::removeLast(){
if (m_size == 0){
cout << "删除位置非法!" << endl;
return NULL;
}
m_size--;
if (m_size < m_capacity / 4){
resize(m_capacity / 2);
}
return m_data[m_size];
}
template <class T>
void Array<T>::removeElement(T num){
int flag = find(num);
if (flag != -1){
remove(flag);
}
}
template <class T>
void Array<T>::set(int index, T num){
if (index < 0 || index >= m_size){
cout << "修改位置非法!" << endl;
return;
}
m_data[index] = num;
}
template <class T>
T Array<T>::get(int index){
if (index < 0 || index >= m_size){
cout << "访问位置非法!" << endl;
return NULL;
}
return m_data[index];
}
template <class T>
int Array<T>::find(T num){
for (int i = 0; i < m_size; ++i){
if (m_data[i] == num){
return i;
}
}
return -1;
}
template <class T>
bool Array<T>::contains(T num){
for (int i = 0; i < m_size; ++i){
if (m_data[i] == num){
return true;
}
}
return false;
}
#endif
抽象类 接口代码:
#ifndef __QUEUE_H__
#define __QUEUE_H__
template <class T>
class Queue{
public:
virtual int size() = 0;
virtual bool isEmpty() = 0;
virtual void print() = 0;
//入队操作
virtual void enqueue(T num) = 0;
//出队操作
virtual void dequeue() = 0;
//获得队首元素
virtual T front() = 0;
};
#endif
数组队列 代码:
#ifndef __ARRAYQUEUE_H__
#define __ARRAYQUEUE_H__
#include "Array.h"
#include "Queue.h"
template <class T>
class ArrayQueue : public Queue<T>{
public:
ArrayQueue(int len = initialLen){
Array<T> *p = new Array<T>(len);
arr = p;
}
int size(){
return arr->size();
}
bool isEmpty(){
return arr->size() == 0;
}
void print(){
cout << "ArrayQueue: ";
cout << "Size = " << arr->size() << endl;
cout << "front[";
for (int i = 0; i < arr->size(); ++i){
cout << arr->get(i);
if (i != arr->size() - 1){
cout << ',';
}
}
cout << "]rear" << endl;
}
//入队操作
void enqueue(T num){
arr->addLast(num);
}
//出队操作
void dequeue(){
arr->removeFirst();
}
//获得队首元素
T front(){
return arr->get(0);
}
private:
Array<T> *arr;
};
#endif