C#實(shí)現(xiàn)優(yōu)先隊(duì)列和堆排序_第1頁
C#實(shí)現(xiàn)優(yōu)先隊(duì)列和堆排序_第2頁
C#實(shí)現(xiàn)優(yōu)先隊(duì)列和堆排序_第3頁
C#實(shí)現(xiàn)優(yōu)先隊(duì)列和堆排序_第4頁
C#實(shí)現(xiàn)優(yōu)先隊(duì)列和堆排序_第5頁
已閱讀5頁,還剩5頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第C#實(shí)現(xiàn)優(yōu)先隊(duì)列和堆排序目錄優(yōu)先隊(duì)列1.API2.初級(jí)實(shí)現(xiàn)3.堆的定義二叉堆表示法4.堆的算法上?。ㄓ上轮辽系亩训挠行蚧┫鲁粒ㄓ缮现料碌亩训挠行蚧└倪M(jìn)堆排序1.堆的構(gòu)造2.下沉排序先下沉后上浮

優(yōu)先隊(duì)列

許多應(yīng)用程序都需要處理有序的元素,但不一定要求它們?nèi)坑行?,或是不一定要一次就將它們排序。很多情況下是收集一些元素,處理當(dāng)前鍵值最大的元素,然后再收集更多的元素,再處理當(dāng)前鍵值最大的元素。這種情況下,需要的數(shù)據(jù)結(jié)構(gòu)支持兩種操作:刪除最大的元素和插入元素。這種數(shù)據(jù)結(jié)構(gòu)類型叫優(yōu)先隊(duì)列。

這里,優(yōu)先隊(duì)列基于二叉堆數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),用數(shù)組保存元素并按照一定條件排序,以實(shí)現(xiàn)對(duì)數(shù)級(jí)別的刪除和插入操作。

1.API

優(yōu)先隊(duì)列是一種抽象數(shù)據(jù)類型,它表示了一組值和對(duì)這些值的操作,抽象層使應(yīng)用和實(shí)現(xiàn)隔離開來。

2.初級(jí)實(shí)現(xiàn)

1.無序數(shù)組實(shí)現(xiàn)

優(yōu)先隊(duì)列的insert方法和下壓棧的push方法一樣。刪除最大元素時(shí),遍歷數(shù)組找出最大元素,和邊界元素交換。2.有序數(shù)組實(shí)現(xiàn)

插入元素時(shí),將較大的元素向右移一格(和插入排序一樣)。這樣刪除時(shí),就可以直接pop。

使用鏈接也是一樣的邏輯。

這些實(shí)現(xiàn)總有一種操作需要線性級(jí)別的時(shí)間復(fù)雜度。使用二叉堆可以保證操作在對(duì)數(shù)級(jí)別的時(shí)間完成。

3.堆的定義

數(shù)據(jù)結(jié)構(gòu)二叉堆可以很好地實(shí)現(xiàn)優(yōu)先隊(duì)列地基本操作。在二叉堆數(shù)組中,每個(gè)元素都要保證大于等于另兩個(gè)特定位置地元素。同樣,這兩個(gè)位置地元素又至少要大于等于數(shù)組中另外兩個(gè)元素,以此類推。用二叉樹表示:

當(dāng)一棵二叉樹的每個(gè)結(jié)點(diǎn)都大于等于它的兩個(gè)子節(jié)點(diǎn)時(shí),它被成為堆有序。從任意結(jié)點(diǎn)向上,都能得到一列非遞減的元素;從任意結(jié)點(diǎn)向下,都能得到一列非遞增的元素。根結(jié)點(diǎn)是堆有序的二叉樹中最大的結(jié)點(diǎn)。

二叉堆表示法

這里使用完全二叉樹表示:將二叉樹的結(jié)點(diǎn)按照層級(jí)順序(從上到下,從左往右)放入數(shù)組中,不使用數(shù)組的第一個(gè)位置(為了方便計(jì)算),根結(jié)點(diǎn)在位置1,它的子結(jié)點(diǎn)在位置2和3,子結(jié)點(diǎn)的子結(jié)點(diǎn)分別在位置4,5,6,7,一次類推。

在一個(gè)二叉堆中,位置k的結(jié)點(diǎn)的父節(jié)點(diǎn)位置在k/2,而它的兩個(gè)子結(jié)點(diǎn)在2k和2k+1??梢酝ㄟ^計(jì)算數(shù)組的索引而不是指針就可以在樹中上下移動(dòng)。

一棵大小為N的完全二叉樹的高度為lgN。

4.堆的算法

用長度為N+1的私有數(shù)組pq[]表示一個(gè)大小為N的堆。

堆在進(jìn)行插入或刪除操作時(shí),會(huì)打破堆的狀態(tài),需要遍歷堆并按照要求將堆的狀態(tài)恢復(fù)。這個(gè)過程稱為堆的有序化。

堆的有序化分為兩種情況:當(dāng)某個(gè)結(jié)點(diǎn)的優(yōu)先級(jí)上升(或在堆底加入一個(gè)新的元素)時(shí),需要由下至上恢復(fù)堆的順序;當(dāng)某個(gè)結(jié)點(diǎn)的優(yōu)先級(jí)下降(例如將根節(jié)點(diǎn)替換為一個(gè)較小的元素),需要由上至下恢復(fù)堆的順序。

上浮(由下至上的堆的有序化)

當(dāng)某個(gè)結(jié)點(diǎn)比它的父結(jié)點(diǎn)更大時(shí),交換它和它的父節(jié)點(diǎn),這個(gè)結(jié)點(diǎn)交換到它父節(jié)點(diǎn)的位置。但有可能比它現(xiàn)在的父節(jié)點(diǎn)大,需要繼續(xù)上浮,直到遇到比它大的父節(jié)點(diǎn)。(這里不需要比較這個(gè)子結(jié)點(diǎn)和同級(jí)的另一個(gè)子結(jié)點(diǎn),因?yàn)榱硪粋€(gè)子結(jié)點(diǎn)比它們的父結(jié)點(diǎn)?。?/p>

//上浮

privatevoidSwim(intn)

while(n1Less(n/2,n))

Exch(n/2,n);

n=n/2;

}

下沉(由上至下的堆的有序化)

當(dāng)某個(gè)結(jié)點(diǎn)k變得比它的兩個(gè)子結(jié)點(diǎn)(2k和2k+1)更小時(shí),可以通過將它和它的兩個(gè)子結(jié)點(diǎn)較大者交換來恢復(fù)堆有序。交換后在子結(jié)點(diǎn)處可能繼續(xù)打破堆有序,需要繼續(xù)重復(fù)下沉,直到它的子結(jié)點(diǎn)都比它小或到達(dá)底部。

//下沉

privatevoidSink(intk)

while(2*k=N)

intj=2*k;

//取最大的子節(jié)點(diǎn)

if(jNLess(j,j+1))

j++;

//如果父節(jié)點(diǎn)不小子節(jié)點(diǎn),退出循環(huán)

if(!Less(k,j))

break;

//否則交換,繼續(xù)下沉

Exch(j,k);

k=j;

}

知道了上浮和下沉的邏輯,就可以很好理解在二叉堆中插入和刪除元素的邏輯。

插入元素:將新元素加到數(shù)組末尾,增加堆的大小并讓這個(gè)新元素上浮到合適的位置。

刪除最大元素:從數(shù)組頂端(即pq[1])刪除最大元素,并將數(shù)組最后一個(gè)元素放到頂端,減少數(shù)組大小并讓這個(gè)元素下沉到合適位置。

publicclassMaxPriorityQueue

privateIComparable[]pq;

publicintN;

publicMaxPriorityQueue(intmaxN)

pq=newIComparable[maxN+1];

publicboolIsEmpty()

returnN==0;

publicvoidInsert(IComparablevalue)

pq[++N]=value;

Swim(N);

publicIComparableDeleteMax()

IComparablemax=pq[1];

Exch(1,N--);

pq[N+1]=null;

Sink(1);

returnmax;

//下沉

privatevoidSink(intk)

while(2*k=N)

intj=2*k;

//取最大的子節(jié)點(diǎn)

if(jNLess(j,j+1))

j++;

//如果父節(jié)點(diǎn)不小子節(jié)點(diǎn),退出循環(huán)

if(!Less(k,j))

break;

//否則交換,繼續(xù)下沉

Exch(j,k);

k=j;

//上浮

privatevoidSwim(intn)

while(n1Less(n/2,n))

Exch(n/2,n);

n=n/2;

privatevoidExch(inti,intj)

IComparabletemp=pq[i];

pq[i]=pq[j];

pq[j]=temp;

privateboolLess(inti,intj)

returnpq[i].CompareTo(pq[j])

}

上述算法對(duì)優(yōu)先隊(duì)列的實(shí)現(xiàn)能夠保證插入和刪除最大元素這兩個(gè)操作的用時(shí)和隊(duì)列的大小成對(duì)數(shù)關(guān)系。這里省略了動(dòng)態(tài)調(diào)整數(shù)組大小的代碼,可以參考下壓棧。

對(duì)于一個(gè)含有N個(gè)元素的基于堆的優(yōu)先隊(duì)列,插入元素操作只需要不超過(lgN+1)次比較,因?yàn)镹可能不是2的冪。刪除最大元素的操作需要不超過2lgN次比較(兩個(gè)子結(jié)點(diǎn)的比較和父結(jié)點(diǎn)與較大子節(jié)點(diǎn)的比較)。

對(duì)于需要大量混雜插入和刪除最大元素的操作,優(yōu)先隊(duì)列很適合。

改進(jìn)

1.多叉堆

基于數(shù)組表示的完全三叉樹:對(duì)于數(shù)組1至N的N個(gè)元素,位置k的結(jié)點(diǎn)大于等于位于3k-1,3k,3k+1的結(jié)點(diǎn),小于等于位于(k+1)/3的結(jié)點(diǎn)。2.調(diào)整數(shù)組大小

使用動(dòng)態(tài)數(shù)組,可以構(gòu)造一個(gè)無需關(guān)注隊(duì)列大小的優(yōu)先隊(duì)列??梢詤⒖枷聣簵!?.索引優(yōu)先隊(duì)列

在許多應(yīng)用程序中,允許客戶端引用優(yōu)先級(jí)隊(duì)列中已經(jīng)存在的項(xiàng)目是有意義的。一種簡單的方法是將唯一的整數(shù)索引與每個(gè)項(xiàng)目相關(guān)聯(lián)。

堆排序

我們可以把任意優(yōu)先隊(duì)列變成一種排序方法:先將所有元素插入一個(gè)查找最小元素的優(yōu)先隊(duì)列,再重復(fù)調(diào)用刪除操作刪除最小元素來將它們按順序刪除。這種排序成為堆排序。

堆排序的第一步是堆的構(gòu)造,第二步是下沉排序階段。

1.堆的構(gòu)造

簡單的方法是利用前面優(yōu)先隊(duì)列插入元素的方法,從左到右遍歷數(shù)組調(diào)用Swim方法(由上算法所需時(shí)間和NlogN成正比)。一個(gè)更聰明高效的方法是,從右(中間位置)到左調(diào)用Sink方法,只需遍歷一半數(shù)組,因?yàn)榱硪话胧谴笮?的堆。這種方法只需少于2N次比較和少于N次交換。(堆的構(gòu)造過程中處理的堆都比較小。例如,要構(gòu)造一個(gè)127個(gè)元素的數(shù)組,需要處理32個(gè)大小為3的堆,16個(gè)大小為7的堆,8個(gè)大小為15的堆,4個(gè)大小為31的堆,2個(gè)大小為63的堆和1個(gè)大小為127的堆,因此在最壞情況下,需要32*1+16*2+8*3+4*4+2*5+1*6=120次交換,以及兩倍的比較)。

2.下沉排序

堆排序的主要工作在第二階段。將堆中最大元素和堆底元素交換,并下沉至N--。相當(dāng)于刪除最大元素并將堆底元素放至堆頂(優(yōu)先隊(duì)列刪除操作),將刪除的最大元素放入空出的數(shù)組位置。

publicclassMaxPriorityQueueSort

publicstaticvoidSort(IComparable[]pq)

intn=pq.Length;

for(vark=n/2;kk--)

Sink(pq,k,n);

//上浮需要遍歷全部

//for(vark=n;kk--)

//Swim(pq,k);

while(n1)

Exch(pq,1,n--);

Sink(pq,1,n);

privatestaticvoidSwim(IComparable[]pq,intn)

while(n1Less(pq,n/2,n))

Exch(pq,n/2,n);

n=n/2;

//下沉

privatestaticvoidSink(IComparable[]pq,intk,intN)

while(2*k=N)

intj=2*k;

//取最大的子節(jié)點(diǎn)

if(jNLess(pq,j,j+1))

j++;

//如果父節(jié)點(diǎn)不小子節(jié)點(diǎn),退出循環(huán)

if(!Less(pq,k,j))

break;

//否則交換,繼續(xù)下沉

Exch(pq,j,k);

k=j;

privatestaticvoidExch(IComparable[]pq,inti,intj)

IComparabletemp=pq[i-1];

pq[i-1]=pq[j-1];

pq[j-1]=temp;

privatestaticboolLess(IComparable[]pq,inti,intj)

returnpq[i-1].CompareTo(pq[j-1])

publicstaticvoidShow(IComparable[]a)

for(vari=0;ia.Length;i++)

Console.WriteLine(a[i]);

}

堆排序的軌跡

將N個(gè)元素排序,堆排序只需少于(2NlgN+2N)次比較以及一半次數(shù)的交換。2N來自堆的構(gòu)造,2NlgN是每次下沉操作最多需要2lgN次比較。

先下沉后上浮

在排序過程中,大多數(shù)重新插入堆中的項(xiàng)目都會(huì)一直到達(dá)底部。因此,通過避免檢查元素是否已到達(dá)其位置,可以簡單地提升兩個(gè)子結(jié)點(diǎn)中的較大者直到到達(dá)底部,然后上浮到適當(dāng)位置,從

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論