数据结构实现 6.4:优先队列_基于链表实现(C++版)
1. 概念及基本框架
6.3 中通过 动态数组 实现了 优先队列 ,这一节我们通过链表来实现优先队列。
优先队列 是 队列 的一种,所以有队列的基本特性:
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 LinkedListPriorityQueue{
...
private:
LinkedList<T> list;
};
同样,这里为了避免重复设计就可以兼容更多数据类型,引入了 泛型 ,即 模板 的概念。(模板的关键字是 class 或 typename)
这里的 list 表示一个 链表 ,同样,为了保护数据,变量设置为 private 。
注:这里没有显式的给出构造函数,因为子类中除了链表对象之外没有特别需要初始化的东西。编译器会默认先调用 链表 类(即父类)的构造函数,再去调用 优先队列 类(即子类)的构造函数。
实现了前面的程序之后,接下来就是一个队列的增、删、查以及一些其他基本操作,接下来利用代码去实现。
2. 基本操作程序实现
2.1 入队操作
template <class T>
class LinkedListPriorityQueue{
public:
...
//入队操作
void enqueue(T num){
list.addFirst(num);
}
...
};
2.2 出队操作
template <class T>
class LinkedListPriorityQueue{
public:
...
//出队操作
void dequeue(){
if (list.isEmpty()){
cout << "出队失败!" << endl;
return;
}
list.remove(findMax());
}
...
private:
int findMax(){
int index = 0;
for (int i = 0; i < list.size(); ++i){
if (list.get(i) > list.get(index)){
index = i;
}
}
return index;
}
...
这里利用了一次遍历查找最大元素。
2.3 查找操作
template <class T>
class LinkedListPriorityQueue{
public:
...
//获得队首元素
T front(){
return list.getFirst();
}
private:
int findMax(){
int index = 0;
for (int i = 0; i < list.size(); ++i){
if (list.get(i) > list.get(index)){
index = i;
}
}
return index;
}
...
};
这里利用了一次遍历查找最大元素。
2.4 其他操作
优先队列还有一些其他的操作,包括 队列大小 等的查询等操作。
template <class T>
class LinkedListPriorityQueue{
public:
int size(){
return list.size();
}
bool isEmpty(){
return list.isEmpty();
}
void print(){
if (list.isEmpty()){
return;
}
cout << "LinkedListPriorityQueue: ";
cout << "Size = " << list.size() << endl;
cout << "front [";
cout << list.get(findMax()) << "...";
cout << "] rear" << endl;
}
...
};
3. 算法复杂度分析
这里入队使用直接插入,出队时遍历查找的方法实现优先队列。
3.1 入队操作
函数 | 最坏复杂度 | 平均复杂度 |
---|---|---|
enqueue | O(1) | O(1) |
3.2 出队操作
函数 | 最坏复杂度 | 平均复杂度 |
---|---|---|
dequeue | O(n) | O(n/2) = O(n) |
3.3 查找操作
函数 | 最坏复杂度 | 平均复杂度 |
---|---|---|
front | O(n) | O(n/2) = O(n) |
总体情况:
操作 | 时间复杂度 |
---|---|
增 | O(1) |
删 | O(n) |
查 | O(n) |
也可以采用入队时遍历放入适当位置,出队出队头。
4. 完整代码
程序完整代码(这里使用了头文件的形式来实现类)如下:
优先队列接口函数一览:
函数声明 | 函数类型 | 函数功能 |
---|---|---|
int size() | public | 返回二叉堆的大小 |
bool isEmpty() | public | 返回优先队列是否为空(空为true) |
void print() | public | 打印输出优先队列队头 |
void enqueue(T) | public | 入队元素到优先队列 |
void dequeue() | public | 从优先队列中出队一个元素 |
T front() | public | 获得优先队列队头元素 |
抽象类 接口代码:
#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 __LINKEDLISTPRIORITYQUEUE_H__
#define __LINKEDLISTPRIORITYQUEUE_H__
#include "Queue.h"
#include "LinkedList.h"
template <class T>
class LinkedListPriorityQueue{
public:
int size(){
return list.size();
}
bool isEmpty(){
return list.isEmpty();
}
void print(){
if (list.isEmpty()){
return;
}
cout << "LinkedListPriorityQueue: ";
cout << "Size = " << list.size() << endl;
cout << "front [";
cout << list.get(findMax()) << "...";
cout << "] rear" << endl;
}
//入队操作
void enqueue(T num){
list.addFirst(num);
}
//出队操作
void dequeue(){
if (list.isEmpty()){
cout << "出队失败!" << endl;
return;
}
list.remove(findMax());
}
//获得队首元素
T front(){
return list.getFirst();
}
private:
int findMax(){
int index = 0;
for (int i = 0; i < list.size(); ++i){
if (list.get(i) > list.get(index)){
index = i;
}
}
return index;
}
private:
LinkedList<T> list;
};
#endif