1. 程式人生 > 其它 >索引優先佇列-IndexedPrirotyQueue的原理及實現(原始碼)

索引優先佇列-IndexedPrirotyQueue的原理及實現(原始碼)

1.索引優先佇列的意義

索引優先佇列是一個比較抽象的概念,它是一個優先佇列,又帶有索引,這個索引是用來幹什麼的呢?

在正常的佇列中,我們只能訪問佇列頭元素,整個佇列中的元素我們都無法訪問。那麼對於這些佇列中的元素,如果我們有一個對映,能夠知道佇列中的第m個元素到底對應我們把所有元素加入優先佇列之前的哪一個,那要使用它豈不是方便許多?

我們知道,在優先佇列中,一個元素加入佇列之後的順序不是固定的,有可能上浮或者下沉。那麼,我們怎麼知道我們加入佇列的這個元素,到底在佇列中的什麼位置呢?

這就是索引優先佇列的用途。它用一個索引陣列儲存了某個元素在優先佇列中的位置

例如:

prirotyQueue[i] = j; //索引優先佇列的第i個元素的值是j
Index[j] = i; //那麼,我們需要的這個元素j在優先佇列中的位置就是i

這樣,當我們需要用這個元素j的時候,我們就能直接找到j的位置是優先佇列的第i個。


2.索引優先佇列的實際應用

(1)從上面的內容看,好像我們知道這個索引也沒什麼實際用處,真的是這樣嗎?

考慮下面一種情況,比如李雷考了全班第一,韓梅梅考了第二。我們把全班四十個人的成績按照高低排了優先佇列。但是複核的時候,突然發現韓梅梅的成績少算了10分,加上10分應該她是第一。那麼,如果沒有這個索引,我們要怎麼修改已經形成的優先佇列呢?

有的人可能說,很簡單啊,把李雷和韓梅梅出佇列,然後更改成績,重新加入佇列。

好,那麼,假如韓梅梅成績統計錯了,她是全班第三十九人呢?難道要把39個人的成績重新出佇列,然後重新加入

嗎?這個成本代價似乎有點高。

更進一步,如果是全校四千人的佇列呢?如果有一千人的成績全算錯了呢?我們要重新生成這個佇列一千次?

這時候,索引優先佇列就有了用武之地。如果韓梅梅的成績錯了,我們從索引裡知道她是優先佇列裡的第二個,那麼我們直接修改她的成績,然後上浮或者下沉就可以了,要付出的代價非常小。

(2)再進一步,如果要按成績排隊之後,依次請家長上臺傳授家教經驗呢?我們怎麼知道每個人的家長是誰啊?

這時候,我們再用一個數組key[],其中儲存了每個人和家長的名字對應關係。這樣來看,有一個索引的用處是不是更大了?

把這些合起來,就得到第三部分的索引優先佇列IndexedPQ的實現。


3.索引優先佇列的API和實現

在很多應用中,允許用例引用已經進入有限佇列中的元素是有必要的。做到這一點的一種簡單方法是用例已經有了總量為N的多個元素,而且可能還同時使用了多個(平行)陣列(Parallel Array)來儲存這些元素的資訊。此時,其他無關的用例程式碼可能已經在使用一個整數索引來引用這些元素了。這些考慮引導我們設計了下表。

理解這種資料結構的一個較好方法是將它看成一個能夠快速訪問其中最小元素的陣列。事實上它還要更好——它能夠快速訪問陣列的一個特定子集中的最小元素(指所有被插入的元素)。換句話說,可以將名為pq的IndexMinPQ優先佇列看做陣列pq[0..N - 1]中的一部分元素的代表。將pq.insert(k, item)看做將k加入這個子集並使pq[k] = item, pq.change(k, item)則代表令pq[k] = item。這兩種操作沒有改變其他操作所依賴的資料結構,其中最重要的就是delMin()(刪除最小元素並返回它的索引)和change()(改變資料結構中的某個元素的索引——即pq[i] = item)。這些操作在許多應用中都很重要並且依賴於對元素的引用(索引)。一般來說,當堆發生變化時,我們會用下沉(元素減小時)或上浮(元素變大時)操作來恢復堆的有序性。在這些操作中,我們可以用索引查詢元素。能夠定位堆中的任意元素也使我們能夠在API中加入一個delete()操作。

命題Q(續)。在一個大小為N的索引優先佇列中,插入元素(insert)、改變優先順序(change)、刪除(delete)和刪除最大小元素(remove the minimum)操作所需的比較次數和logN成正比(如後表)

證明。已知堆中所有路徑最長即為~lgN,從程式碼中很容易得到這個結論。

操作

比較次數的增長數量級

insert()

logN

change()

logN

contains()

1

delete()

logN

min()

1

minIndex()

1

delMin

logN

以下是《alg4》書中實現的一個找出最大元素的索引優先佇列的JAVA版本IndexMaxPQ.java