堆得簡單介紹以及堆排序


首先看一下堆的定義:

對於n個元素的序列{k1,k2,k3,……,kn},當且僅當滿足下列關系時,稱之為堆:

K(i) <= K(2*i) && K(i) <= K(2*i+1)      此時的堆為小頂堆

K(i) >= K(2*i) && K(i) >= K(2*i+1)      此時的堆為大頂堆

(i = 1,2,……,n/2(下取整))

注意:堆得存儲是用一維數組來存儲的。

若將堆對應的序列看成是一個完全二叉樹,則堆得含義表明:

完全二叉樹中所有非終端結點的值均不大於(或不小於)其左右孩子結點的值。

因此,若序列{K1,K2,……,Kn} 是大頂堆,則堆頂元素必為序列中n個元素的最大值;反之,若序列是小頂堆,則堆頂元素必為序列中n個元素的最小值。

堆排序就是利用的這個性質。

堆排序的過程如下:

假設要從小到大排序,我們構建一個大頂堆,則堆頂元素是最大值。將堆頂元素和最后一個元素互換,則最后一個元素變成了n個元素中的最大值。之后再將剩下的 n-1 個元素調整成為大頂堆,將堆頂元素和第n-1 個元素互換,則第n-1 個元素變成了n個元素中的次大值……循環這個過程,不斷調整堆,最后得到一個有序的序列。

在上面堆排序的過程中,有兩個問題需要解決:

(1)如何將一個初始的序列構建成一個大頂堆?

(2)再得到最大元素后,剩下的n-1個元素如何再次調整成為一個大頂堆?

實際上,初始序列構建大頂堆也是一個不斷調整堆得過程。因此,只要解決第二個問題就可以。

下圖是一個大頂堆:

 

當把堆頂元素20和最后一個元素互換之后,最后一個元素變成了序列中的最大值。如下圖:

 

但是,此時堆頂元素違反了大頂堆的性質,堆頂元素的左右孩子仍舊滿足大頂堆的性質。因此,此時需要對堆進行調整。因為左子樹的值大於右子樹的值,所以將3和17互換,如下圖:

此時,左子樹又違反了大頂堆得性質,所以需要調整左子樹,如下圖:

至此,一次調整完畢,堆頂元素成為了次大元素。

實際上,調整堆就是這樣一個不斷篩選比較的過程,不斷的和左右子樹比較,一直到不需要交換為止。

將一個無序序列構建成一個大頂堆的過程就是一個反復篩選的過程。將此序列看成是一個完全二叉樹,則最后一個非葉子節點是第 n/2(下取整)個元素,因此,篩選只需從第 n/2(下取整)個元素開始。

假設有序列:{49,38,65,97,76,13,27},初始二叉樹是:

 

從第3個元素,也就是65開始調整堆,65大於左右子樹的值,因此不需要調整。

然后是第2個元素,也就是從38開始調整堆,38和左右子樹比較,將97和38互換,調整后如下圖:

 

然后是第1個元素,也就是從49開始調整堆,49和左右子樹比較,將97和49互換,互換之后,因為49破壞了左子樹大頂堆的性質,因此需要繼續調整,將49和左右子樹比較,然后將49和76互換,調整過程如下圖:

 

至此,將一個無序的序列調整成為了一個大頂堆。

同理,堆排序也分為兩個過程:(1)將初始化序列調整成為一個大頂堆(2)用最后一個元素和堆頂元素交換,然后不斷調整剩下的元素成為一個新的大頂堆。

代碼如下:

 

const int N = 8;
int num[N] = {-1,49,38,65,97,76,13,27};  //從第一個元素開始存儲

//調整堆的函數
void heapAdjust(int pos,int total){
	int temp = num[pos];
	for(int j = 2 * pos; j <= total; j *= 2){
		if(j < total){  //說明還有右子樹
			if(num[j] < num[j + 1]){   //篩選出左右子樹中較大的值
				j += 1;
			}
		}
		if(temp >= num[j])  //不需要再繼續向下調整了
			break;
		num[pos] = num[j];
		pos = j;
	}
	num[pos] = temp;
}

void heapSort(){
	//首先將數組構建成一個大頂堆
	for(int i = (N-1) / 2;i >=1;--i){
		heapAdjust(i,N - 1);
	}
	//開始堆排序
	for(int i = N-1; i > 1; --i){
		int temp = num[i];   //交換第一個元素和最后一個元素
		num[i] = num[1];
		num[1] = temp;
		heapAdjust(1,i - 1);  //交換完之后,重新調整堆
	}
}

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM