优先队列的一种实现方式——堆

转载自https://blog.csdn.net/ahafg/article/details/60581286

优先队列

  队列是一个操作受限的线性表,数据只能在一端进入,另一端出来,具有先进先出的性质。有时在队列中需要处理优先级的情况,即后面进入的数据需要提前出来,这里就需要优先队列。优先队列是至少能够提供插入和删除最小值这两种操作的数据结构。对应于队列的操作,插入相当于入队,删除最小相当于出队。
  链表,二叉查找树,都可以提供插入和删除最小这两种操作。对于链表的实现,插入需要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的用法

猜你喜欢

转载自blog.csdn.net/Eartha1995/article/details/81609228