何为优先队列
在简绍优先队列前,我们先来回顾一下普通队列的特点,即“先进先出(FIFO)"。
而优先队列不再遵循先入先出的原则,而是分为两种情况:
- 最大优先队列,无论入队顺序,当前最大的元素优先出队。
- 最小优先队列,无论入队顺序,当前最小的元素优先出队。
比如有一个最大优先队列,它的最大元素是8,那么虽然元素8并不是队首元素,但出队的时候仍然让元素8首先出队:
为了满足优先级队列这一特性,可以使用最大堆或者最小堆。
1.最大堆的堆顶元素是整个堆中的最大元素
2.最小堆的堆顶元素是整个堆中的最小元素
因此,我们可以用最大堆来实现最大优先队列,每一次入队操作就是堆的插入操作,每一次出队操作就是删除堆顶节点。
入队操作:
1.插入新节点5
2.新节点5上浮到合适位置。
出队操作:
1.把原堆顶节点10“出队”
2.最后一个节点1替换到堆顶位置
3.节点1下沉,节点9成为新堆顶
时间复杂度
二叉堆节点上浮和下沉的时间复杂度都是log2n,所以优先队列入队和出队的时间复杂度也是log2n。
最小堆实现
/*优先级队列*/
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stack>
#include<queue>
const int MAXSIZE = 100;
const int INCSIZE = 2;
template<class Type>
class PriQueue
{
Type* data;
int maxsize;//最大容量
int cursize;//当前容量
void FilterDown(int begin, int end)//下沉操作 删除
{
int i = begin, j = i * 2 + 1;
Type tmp = data[i];
while (j <= end)
{
if (j < end && data[j] > data[j + 1]) ++j;
if (tmp <= data[j]) break;
data[i] = data[j];
i = j;
j = i * 2 + 1;
}
data[i] = tmp;
}
void FilterUp(int begin)//上浮操作 插入
{
int j = begin, i = (j - 1) / 2;
Type tmp = data[j];
while (j > 0)
{
if (data[i] <= tmp) break;
data[j] = data[i];
j = i;
i = (j - 1) / 2;
}
data[j] = tmp;
}
void Make_Heap()//构建堆时相当于不断进行下沉操作
{
int end = cursize - 1;
int pos = (end - 1) / 2;
while (pos >= 0)
{
FilterDown(pos, end);
--pos;
}
}
public:
PriQueue() :maxsize(MAXSIZE), cursize(0)//构造函数
{
data = new Type[maxsize];
}
PriQueue(Type* ar, int n)//调用此构造函数时,说明数据已存在,即要调整元素的位置构造堆
{
maxsize = n > MAXSIZE ? n : MAXSIZE;
cursize = n;
data = new Type[maxsize];
for (int i = 0; i < n; ++i)
{
data[i] = ar[i];
}
Make_Heap();
}
~PriQueue()
{
delete[]data;
data = NULL;
maxsize = 0;
cursize = 0;
}
int GetSize() const { return cursize; }//当前节点个数
bool IsEmpty() const { return GetSize() == 0; }//判空
Type& GetFront() //取队顶元素
{
return data[0];
}
const Type& GetFront() const
{
return data[0];
}
void pop()//删除队顶元素
{
data[0] = data[cursize - 1];
--cursize;
FilterDown(0, cursize - 1);
}
void Push(const Type& x)//入队列
{
if (cursize >= maxsize) return;
data[cursize] = x;
FilterUp(cursize);
++cursize;
}
};
int main()
{
PriQueue<int> pqu;
int x;
while (std::cin >> x, x != -1)
{
pqu.Push(x);
}
while (!pqu.IsEmpty())
{
int x = pqu.GetFront(); pqu.pop();
std::cout << x <<std:: endl;
}
return 0;
}
执行结果:
最大堆实现
/*优先级队列*/
/*小根堆*/
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
const int MAXSIZE = 100;
const int INCSIZE = 2;
template<class Type>
class PriQueue
{
Type* data;
int maxsize;//最大容量
int cursize;//当前容量
void FilterDown(int begin, int end)//下沉操作 删除 大根堆
{
int i = begin, j = i * 2 + 1;//j是左孩子
Type tmp = data[i];
while (j <= end)
{
if (j < end && data[j] < data[j + 1]) ++j;
if (tmp >= data[j]) break;
data[i] = data[j];
i = j;
j = i * 2 + 1;
}
data[i] = tmp;
}
void FilterUp(int begin)//上浮操作 插入 大根堆
{
int j = begin, i = (j - 1) / 2;
Type tmp = data[j];
while (j > 0)
{
if (data[i] >= tmp) break;
data[j] = data[i];
j = i;
i = (j - 1) / 2;
}
data[j] = tmp;
}
void Make_Heap()//构建堆时相当于不断进行下沉操作
{
int end = cursize - 1;
int pos = (end - 1) / 2;
while (pos >= 0)
{
FilterDown(pos, end);
--pos;
}
}
public:
PriQueue() :maxsize(MAXSIZE), cursize(0)//构造函数
{
data = new Type[maxsize];
}
PriQueue(Type* ar, int n)//调用此构造函数时,说明数据已存在,即要调整元素的位置构造堆
{
maxsize = n > MAXSIZE ? n : MAXSIZE;
cursize = n;
data = new Type[maxsize];
for (int i = 0; i < n; ++i)
{
data[i] = ar[i];
}
Make_Heap();
}
~PriQueue()
{
delete[]data;
data = NULL;
maxsize = 0;
cursize = 0;
}
int GetSize() const { return cursize; }//当前节点个数
bool IsEmpty() const { return GetSize() == 0; }//判空
Type& GetFront() //取队顶元素
{
return data[0];
}
const Type& GetFront() const
{
return data[0];
}
void pop()//删除队顶元素
{
data[0] = data[cursize - 1];//最后一个节点补到堆顶
--cursize;
FilterDown(0, cursize - 1);//从堆顶开始向下调整
}
void Push(const Type& x)//入队列
{
if (cursize >= maxsize) return;
data[cursize] = x;
FilterUp(cursize);//数组的最后一个位置
++cursize;
}
};
int main()
{
PriQueue<int> pqu;
int x;
while (std::cin >> x, x != -1)
{
pqu.Push(x);
}
while (!pqu.IsEmpty())
{
int x = pqu.GetFront(); pqu.pop();
std::cout << x <<std:: endl;
}
return 0;
}
执行结果: