优先队列
队列是一个操作受限的线性表,数据只能在一端进入,另一端出来,具有先进先出的性质。有时在队列中需要处理优先级的情况,即后面进入的数据需要提前出来,这里就需要优先队列。优先队列是至少能够提供插入和删除最小值这两种操作的数据结构。对应于队列的操作,插入相当于入队,删除最小相当于出队。
链表,二叉查找树,都可以提供插入和删除最小这两种操作。对于链表的实现,插入需要O(1),删除最小需要遍历链表,故需要O(N)。对于二叉查找树,这两种操作都需要O(logN);而且随着不停的删除最小的操作,二叉查找树会变得非常不平衡;同时使用二叉查找树有些浪费,因此很多操作根本不需要。一种较好的实现优先队列的方式是二叉堆(下面简称堆)。
堆
堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。首先堆是完全二叉树(只有最下面的两层结点度能够小于2,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树),其次任意节点的左右孩子(若有)值都不小于其父亲,这是小根堆,即最小的永远在上面。相反的是大根堆,即大的在上面。
因为完全二叉树有很好的规律,因此可以只用数据来存储数据而不需要链表。如下图:
如图保存在数组中的数据A~J,对于任意位置i,其左儿子在数组中位置为2i,其右儿子在数组中位置为2i+1,其父亲位置在floor(i/2)。
堆的插入:插入堆,那么堆中元素将加一,会在最后一个叶子后添加一个叶子,如上图中的J后面。添加了一个叶子后,需要按照大小比较将这个值放到特定的位置,直到满足堆条件,这是个上滤过程。
堆的删除最小:删除堆中最小的一个,对于小根堆来说就是删除根,删除根后,需要将下面的元素按照大小规则往上移,直到删除最后一个叶子,这是个下滤过程。
建堆的时间复杂度为O(N),删除堆最小值的时间复杂度为O(logN),N为节点数。
定义一个堆结构并实现数据插入和删除最小值操作:
#define max 20
struct queue
{
int size;
int data[max];
};
//insert a data to queue;
void insert(queue &q, int x)
{
int tmp = q.size; //store data at the first site data[0]
q.data[tmp] = x;
while ((tmp-1)/2>=0)
{
if(q.data[tmp] < q.data[(tmp-1)/2])
{
q.data[tmp] = q.data[(tmp-1)/2];
q.data[(tmp-1)/2] = x;
tmp = (tmp-1)/2;
}else break; //
}
q.size++;
}
//delete the minimum data in the queue
void deletemin(queue &q)
{
int tmp = 0;
while (tmp*2+2 < q.size)
{
if(q.data[tmp*2+1]<q.data[tmp*2+2])
{
q.data[tmp] = q.data[tmp*2+1];
tmp = tmp*2+1;
}
else
{
q.data[tmp] = q.data[tmp*2+2];
tmp = tmp*2+2;
}
}
q.data[tmp] = q.data[q.size-1];
q.data[q.size-1] = 0;
q.size--;
}
int main()
{
queue q;
q.size = 0; //it's necessary
insert(q,7);insert(q,5);insert(q,2);
insert(q,3);insert(q,1);insert(q,4);
deletemin(q);
for(int i=0; i<q.size; i++)
cout << q.data[i];
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
参考:
优先队列及最小堆最大堆
Priority Queue(Heaps)–优先队列(堆)
STL中的优先队列:priority_queue
priority_queue为STL中实现的优先队列结构,输入存入队列会自动按照大根堆的性质保存。它的模板声明带有三个参数:
priority_queue<Type, Container, Functional>
Type 为数据类型,Container 为保存数据的容器,Functional 为元素比较方式。Container 必须是用数组实现的容器,比如 vector, deque 但不能用 list。STL里面默认用的是 vector,比较方式默认用 operator< , 所以如果你把后面俩个参数缺省的话,优先队列就是大顶堆,队头元素最大。
priority_queue<int,vector<int>,greater<int> >q3
;//定义小的先出队
常用的函数:
push() //插入一个数;
pop() //删除最小(大)的那个数;
//front() //返回队首元素;
//back() //返回队尾元素;
top() //返回队顶元素,没有上面两个,queue才有
empty() //队列是否为空
size() //队列中元素个数
- 1
- 2
- 3
- 4
- 5
- 6
- 7
priority_queue定义在头文件queue中,在此文件中还有一种数据类型queue,即单向队列,基本操作也是上面的几个函数,与双向队列deque略有差别。
基本定义:queue<int> q;
int main()
{
queue<int> s;
priority_queue<int> ss;
priority_queue<int,vector<int>,greater<int> >q3;
s.push(1);s.push(2); s.pop();
ss.push(1);ss.push(4);ss.push(0);ss.pop();
q3.push(1);q3.push(4);q3.push(0);q3.pop();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
需要注意的是:优先队列priority_queue是不支持迭代器的运算的,读取元素只能在队列首部读取(top), queue也是一样的不支持,因此不能对queue进行排序,只能读取队首和队尾的数据(front,back()),同样的stack也是一样不能使用迭代器,只能读取栈顶的数据。
对于自己定义的数据类型作为队列参数,用法参考:priority_queue的用法