優先隊列:
優先隊列時一種數據結構,其數據項中帶有關鍵字,支持兩種基本操作:
- 向優先隊列中插入一個新的數據項;
- 刪除優先隊列中的關鍵字最大的數據項;
優先隊列數組實現:
將記錄存儲在一個無序數組中,從數組的末尾插入和刪除數據項。
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。
堆數據結構:
堆是一個節點的集合,表示爲數組,其中關鍵字按照堆有序的完全二叉樹的形式排列。
如果一棵樹中的每個節點的關鍵字都大於或等於所有子節點中的關鍵字,則稱樹是堆有序的。同樣,一棵堆有序樹中節點的關鍵字小於等於那個節點父節點的關鍵字。
基於堆的算法:
堆化/修正堆:
- 當某個節點的優先級增加時(或新的節點被添加到堆底時),我們必須向上遍歷以恢復堆的條件。
- 當某個節點的優先級降低時(如我們用一個新的節點代替根節點時),我們必須向下遍歷堆以恢復堆的條件。
如果由於一個節點的關鍵字大於那個節點父節點的關鍵字,堆的性質受到侵犯,那麼我們可以交換該節點與它的父節點來修正堆。以此類推,沿堆向上進行,直到到達一個較大關鍵字的節點,或者到達根節點。
自底向上堆化:
在增加一個父節點的優先級時,爲了恢復堆的條件,我們在堆中向上移動,需要時交換位置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進行刪除。
}