【数据结构】优先队列和堆
优先队列(priority queue)
对于一般的队列是在队列的尾部添加数据,在头部删除,以便先进先出。
而优先队列中的每个元素都有一个优先级,每次将一个元素放入队列中,而每次出队时都是将优先级最大的那个元素出队,称为高进先出(largest-in,first-out)。
优先队列必须实现以下几个操作
1.入队(push):将一个元素放入队列中。
2.出队(pop):将优先级最高的元素从队列中删除。
要实现上面的操作可以维护一个有序的链表。每次插入数据时维护整个链表有序,即使用插入法,每次出队的操作只需要删除链表最前面的元素。
这么做的话入队操作的复杂度是O(n),出队是O(1),综合复杂度为O(n)。
接下来要介绍的是O(logn) 的数据结构——二叉堆。
给定一棵完全二叉树,该树的每个节点的优先级大于该节点的所有孩子节点的优先级。易知,根节点将是优先级最高的节点。
要用二叉堆来实现优先队列,必须完成的两个操作:
1.入队(push):对于一棵空的二叉堆,只需要将入堆的元素加入根节点即可。对于一般情况,我们将该元素直接加入完全二叉树的一个位置,该位置是指将这个位置加入该完全二叉树,这个树仍然是完全二叉树的那个点。之后将这个元素和他的父节点比较,若优先级大于该父节点,则与父节点交换位置,再将新插入的这个元素再和它的新父节点比较,直到它没有父节点或有一个父节点优先级大于它为止。
2.出队(pop):要拿出该完全二叉树的根节点,之后要维护剩下的节点满足二叉堆的性质。
在讲这个操作之前,我们先定义一个新的操作——假设某节点的左右两棵子树都满足堆的性质,要将整个树也变成一个堆。我们把该操作称为筛选(sift)。对于筛选算法我们需要将根节点和两个子节点比较,若根节点的优先级最大,则已经调整完成,否则将两个子节点中较大的那个和根节点进行交换,此时根节点将是最大的元素了,然后跟踪被交换的那个孩子节点的位置,继续做如上的调整,直到调整结束。
对于出队操作,删除根节点的元素,并将该二叉堆的最后一个元素(所谓最后一个元素是指将该元素从完全二叉树删除,该二叉树仍然是完全二叉树的那个元素)放到根节点的位置,然后对该树进行筛选操作。
一般的来讲,堆可以轻易地用数组实现,假设数组编号为1的元素是根节点,那么2和3分别是它的左右节点。对于一个数组编号为i(i>=1)的节点,他的左右节点分别为i*2和i*2+1。
要将一个数组中的n个元素(编号从1到n)直接变成堆,可以用以下操作:for(i=n/2;i>=1;i--) sift(以编号为i的节点为根节点)。这是因为超过n/2的那些节点都是叶子节点,满足堆的性质,其他节点从下往上依次进行sift操作就完成的堆的建立。
1 #include<stdio.h> 2 /*r[0].fix为堆的长度*/ 3 typedef int elemtype; 4 typedef struct node 5 { 6 elemtype data; 7 int fix; 8 }HeapNode; 9 HeapNode out(HeapNode r[],int s)/*r[s]出堆*/ 10 { 11 int i,j; 12 HeapNode item; 13 item=r[s];/*数据暂存*/ 14 r[s]=r[r[0].fix];/*最后一个数据放入要出堆的元素的位置上*/ 15 r[0].fix--;/*长度减一*/ 16 sift(r,s,r[0]); 17 return item; 18 } 19 void in(HeapNode r[],HeapNode item)/*入堆*/ 20 { 21 int i; 22 for(i=r[0].fix+1;i>1;i/=2) 23 { 24 if(item.fix</*>*/r[i/2].fix) r[i]=r[i/2]; 25 else break; 26 } 27 r[i]=item; 28 r[0].fix++; 29 } 30 void sift(HeapNode r[],int s,int t)/*筛选成小顶堆算法*/ 31 {/*r[s...t]中的记录除r[s]外均满足堆的特性*/ 32 int i,j; 33 HeapNode item; 34 item=r[s]; 35 i=s; 36 j=2*i; 37 while(j<=r[0].fix) 38 { 39 if(j+1<=r[0].fix&&r[j+1].fix</*>*/r[j].fix) j++; 40 if(item.fix>/*<*/r[j].fix){r[i]=r[j];i=j;j=2*i;} 41 else break; 42 } 43 r[i]=item; 44 } 45 /* 46 初始堆的建立: 47 for(i=n/2;i>=1;--) sift(r,i,n); 48 */ 49 /*堆排序*/ 50 /*大顶堆完成升序,小顶堆完成降序*/ 51 void Heat_sort(long r[],int n) 52 { 53 int i; 54 long item; 55 for(i=n/2;i>=1;i--) sift(r,i,n);/*初始堆的建立*/ 56 for(i=n;i>=2;i--) 57 { 58 item=r[i]; 59 r[i]=r[1]; 60 r[1]=item; 61 sift(r,1,i-1); /*大顶堆完成升序,小顶堆完成降序*/ 62 } 63 }