优先队列:
优先队列时一种数据结构,其数据项中带有关键字,支持两种基本操作:
- 向优先队列中插入一个新的数据项;
- 删除优先队列中的关键字最大的数据项;
优先队列数组实现:
将记录存储在一个无序数组中,从数组的末尾插入和删除数据项。
static Item *pq; static int N; void PQinit(int maxN) { pq = malloc(maxN * sizeof(Item));//数组分配空间 N = 0; } int PQempty() { return N == 0; } int PQinsert(Item v) { pq[N++] = v; } Item PQdelmax() { int j; int max = 0; for(j=1;j<N;j++)//找出最大元素 { if(less(pq[max],pq[j])) max = j; } exch(pq[max],pq[N-1]);//将最大元素与最后元素位置交换 return pq[--N];//从末尾删除最大元素。 }
若维持一个有序的序列,可允许使用常量时间进行deleteMAX和findMAX,但是insert时需要遍历整个表。
若维持一个无序的序列,insert需要常量时间,但是需要遍历整个序列执行deleteMAX和findMAX。
堆数据结构:
堆是一个节点的集合,表示为数组,其中关键字按照堆有序的完全二叉树的形式排列。
如果一棵树中的每个节点的关键字都大于或等于所有子节点中的关键字,则称树是堆有序的。同样,一棵堆有序树中节点的关键字小于等于那个节点父节点的关键字。
基于堆的算法:
堆化/修正堆:
- 当某个节点的优先级增加时(或新的节点被添加到堆底时),我们必须向上遍历以恢复堆的条件。
- 当某个节点的优先级降低时(如我们用一个新的节点代替根节点时),我们必须向下遍历堆以恢复堆的条件。
如果由于一个节点的关键字大于那个节点父节点的关键字,堆的性质受到侵犯,那么我们可以交换该节点与它的父节点来修正堆。以此类推,沿堆向上进行,直到到达一个较大关键字的节点,或者到达根节点。
扫描二维码关注公众号,回复:
1659955 查看本文章
自底向上堆化:
在增加一个父节点的优先级时,为了恢复堆的条件,我们在堆中向上移动,需要时交换位置k处节点与其父节点(k/2),只有a[k/2] < a[k]就继续这一过程,或者到达堆顶。
fixUp(Item a[],int k) { while(k>1 && less(a[k/2],a[k]))//父节点 < 子节点 或者 到达堆顶。 { exch(a[k],a[k/2]);//不断向上修正 k = k/2 } }
自顶向下堆化:
在降低一个节点的优先级时,为了恢复堆的条件,我们在堆中向下移动,需要时交换位置k处的节点与其子节点中较大的一个节点,如果k处的节点不小于它的任何一个子节点,或者到达树底,则停止这一过程。
fixDown(Item a[],int k,int N) { int j; while(2*k <= N)//没有到达树底,则向下堆化。若已经到达树底,则结束。 { j = 2*k; if(j < N && less(a[j],a[j+1])//取出较大子节点位置 j++; if( !less(a[k],a[j]) ) //若父节点不大于子节点,则结束 break; exch(a[k],a[j]); k=j; } }
基于堆的优先队列
将优先队列表示成一个堆有序的数组,
insert操作相当于在数组末尾加入新元素,然后将该元素由堆的底部往上移,从而重构堆;
deleteMAX操作相等于把最大元素从堆顶移走,然后把最后的元素移到堆顶,再把它从堆顶下移,从而重构堆。
static Item *pq; static int N; void PQinit(int maxN) { pq = malloc((maxN+1)*sizeof(Item)); N=0; } int PQempty() { return N==0; } void PQinsert(Item v) { pq[++N] = v;//尾部插入 fixUp(pq,N);//自底向上堆化 } Item PQdelmax() { exch(pq[1],pq[N]);//将最大元素交换到尾部 fixDown(pq,1,N-1);//自顶向下堆化 return pq[N--];//返回被交换到最后的最大值,并N减1进行删除。 }