算法导论:堆排序
堆
堆是一個(gè)數(shù)組,它可以被看成一個(gè)近似的完全二叉樹(shù),樹(shù)上的每一個(gè)結(jié)點(diǎn)對(duì)應(yīng)數(shù)組中的一個(gè)元素。除去最底層外,該樹(shù)是完全充滿的,而且從左到右填充。
用數(shù)組A表示堆,從數(shù)組第1個(gè)元素開(kāi)始,數(shù)組中第i(1<=i <=n)個(gè)元素,其父結(jié)點(diǎn),左孩子,右孩子的下標(biāo)如下
// 父結(jié)點(diǎn)public int parent( int i){return i/2;}// 左孩子public int left(int i){return 2*i;}// 右孩子public int right(int i){return 2*i+1;}?當(dāng)數(shù)組起始下標(biāo)是0的時(shí)候,其父結(jié)點(diǎn),左右孩子結(jié)點(diǎn)如下
// 父結(jié)點(diǎn)public int parent( int i){return (i-1)/2;}// 左孩子public int left(int i){return 2*i+1;}// 右孩子public int right(int i){return 2*i+2;}?
堆可以分為大頂堆和小頂堆
大頂堆:結(jié)點(diǎn) i 的值 都大于其左右孩子結(jié)點(diǎn)的值
小頂堆:結(jié)點(diǎn) i 的值 都小于其左右孩子結(jié)點(diǎn)的值
二叉樹(shù)的形式與數(shù)組形式表達(dá)堆之間元素的關(guān)系
?
練習(xí)1,高度為h的堆,元素最少和最多是多少?
最多:這個(gè)完全二叉樹(shù)第h層元素填滿:2^h - 1
最少:第h-1層填滿,第h層只有一個(gè)元素:2^(h-1) -1 + 1 = 2^(h-1)
說(shuō)明:h=1 表示第一層
練習(xí)2,含有n個(gè)元素的堆的高度是 [lgn]
練習(xí)3,當(dāng)用數(shù)組存放堆的時(shí)候,堆的元素時(shí)候n,求葉子結(jié)點(diǎn)下標(biāo)
?
構(gòu)建大頂堆
max-heapify 用于維護(hù)一個(gè)大頂堆。它的輸入是一個(gè)數(shù)組A和下標(biāo)i。在調(diào)用max-heapify的時(shí)候,假定根結(jié)點(diǎn)left(i) 和right(i) 的二叉樹(shù)都是大頂堆,但這時(shí)A[i]有可能小于其中的一個(gè)孩子,這樣就違背大頂堆的性質(zhì)。我們需要進(jìn)行調(diào)整,選取left(i) right(i) 對(duì)應(yīng)結(jié)點(diǎn)較大的一個(gè)和 i 位置處的結(jié)點(diǎn)進(jìn)行互換。
建堆代碼如下
/*** 調(diào)整堆元素* @param A* @param n* @param i*/public void max_heapify(int[] A ,int n,int i){// 左孩子結(jié)點(diǎn)int l = left(i);// 右孩子結(jié)點(diǎn)int r = right(i);// 最大結(jié)點(diǎn)下標(biāo)int largest = -1;// 與左孩子判斷if(l<= n && A[l] > A[i])largest = l;elselargest = i;// 與右孩子判斷if(r <=n && A[r] > A[largest])largest = r;// i 結(jié)點(diǎn)不是最大值maxId 和i 結(jié)點(diǎn)進(jìn)行交換if(largest != i ){swap(A,largest,i); max_heapify(A,n,largest);}}/*** 交換* @param A* @param l* @param r*/public void swap(int [] A,int l,int r){int tmp = A[l];A[l] = A[r];A[r] = tmp;}?
?時(shí)間復(fù)雜度:O(lg(n))
說(shuō)明:
在 i left(i) right(i) 三個(gè)結(jié)點(diǎn)中,選取其對(duì)應(yīng)的值最大的結(jié)點(diǎn)和 i 結(jié)點(diǎn)的值進(jìn)行交換。在交換后,下標(biāo)為largest的結(jié)點(diǎn)的值是原來(lái)的A[i] 的值,largest所在的結(jié)點(diǎn)的子樹(shù)可能違反大頂堆的性質(zhì),需要對(duì)該子樹(shù)遞歸調(diào)用max-heapify
這里的假設(shè)是 以left(i) right(i) 為結(jié)點(diǎn)的堆已經(jīng)是大頂堆,所以這個(gè)時(shí)候,我們只需要上面的三個(gè)元素就好了
算法導(dǎo)論例子
?
改成循環(huán)代碼
/*** 調(diào)整堆元素* @param A 數(shù)組存放堆* @param n 數(shù)組的數(shù)量 從 1 - n 開(kāi)始* @param i 需要調(diào)整的堆 元素 位置 */public void max_heapify1(int[] A ,int n,int i){int l = -1;int r = -1;int largest = -1;while(true){l = left(i);r = right(i);if(l<=n && A[l] > A[i])largest = l;else largest = i;if(r <=n && A[r] > A[largest])largest = r;if(largest!=i){swap(A,largest,i);//更新i 的值相當(dāng)?shù)倪f歸調(diào)用i = largest;// 相等 對(duì)左右子樹(shù)不影響}else{break;} }}?
建堆
自底向上的方法,利用max-heapify 把一個(gè)大小為n 的數(shù)組A[1,...,n] 轉(zhuǎn)換成大頂堆。子數(shù)組A[[n/2] +1,...,n] 中的元素是樹(shù)的葉子結(jié)點(diǎn)。每個(gè)葉子結(jié)點(diǎn)可以看成一個(gè)大頂堆,所有在建堆的時(shí)候從 n/2 的元素開(kāi)始 一直到 第 1 的元素。
為什么這樣做?因?yàn)閷?duì)當(dāng)前結(jié)點(diǎn)進(jìn)行操作時(shí)能夠保證以當(dāng)前結(jié)點(diǎn)為根的樹(shù)的子樹(shù)都已經(jīng)是最大頂堆。這是從后像前的過(guò)程,先把底層的大頂堆構(gòu)建好,再逐步的構(gòu)建上層的大頂堆,在一定程度上減少了不比較的操作。如果是從1 到 n/2 的時(shí)候,對(duì)于新加入的元素,前面已經(jīng)構(gòu)建好的大頂堆可能都需要進(jìn)行更新。比如,我們已經(jīng)在第100層, 新插入的元素比之前堆內(nèi)的元素都大,如100000,前面已經(jīng)構(gòu)建好的99層都要進(jìn)行調(diào)整。
/*** 建立大頂堆* @param A* @param n*/public void build_max_heap(int[] A,int n){for(int i = n/2;i>=1;i--){max_heapify(A,n,i);}}?
時(shí)間復(fù)雜度:O(nlog(n))
算法導(dǎo)論建堆例子
堆排序算法
初始時(shí)候,利用build_max_heap將輸入數(shù)組A[1,..,n] 建成大頂堆,這里A[1]一定是最大的元素,將A[1]與A[n]互換,再對(duì)A[1,...,n-1] 調(diào)整為大頂堆,然后A[1]與A[n-1]元素互換,再對(duì)A[1,...,n-2] 調(diào)整為大頂堆,如此循環(huán)下去
/*** 堆排序 升序* @param A* @param n */public void heap_sort(int[] A,int n){build_max_heap(A,n);for(int i = n;i>=2;i--){swap(A,i,1);max_heapify(A,i-1,1);}}堆排序的時(shí)間復(fù)雜度:O(nlogn)
算法導(dǎo)論例子
?
所有程序
package heapSort;class heap{/*** 堆排序 升序* @param A* @param n */public void heap_sort(int[] A,int n){build_max_heap(A,n);for(int i = n;i>=2;i--){swap(A,i,1);max_heapify(A,i-1,1);}}/*** 建立大頂堆* @param A* @param n*/public void build_max_heap(int[] A,int n){for(int i = n/2;i>=1;i--){max_heapify(A,n,i);}}/*** 調(diào)整堆元素* @param A 數(shù)組存放堆* @param n 數(shù)組的數(shù)量 從 1 - n 開(kāi)始* @param i 需要調(diào)整的堆 元素 位置 */public void max_heapify1(int[] A ,int n,int i){int l = -1;int r = -1;int largest = -1;while(true){l = left(i);r = right(i);if(l<=n && A[l] > A[i])largest = l;else largest = i;if(r <=n && A[r] > A[largest])largest = r;if(largest!=i){swap(A,largest,i);//更新i 的值相當(dāng)?shù)倪f歸調(diào)用i = largest;// 相等 對(duì)左右子樹(shù)不影響}else{break;} }}/*** 調(diào)整堆元素* @param A* @param n* @param i*/public void max_heapify(int[] A ,int n,int i){// 左孩子結(jié)點(diǎn)int l = left(i);// 右孩子結(jié)點(diǎn)int r = right(i);// 最大結(jié)點(diǎn)下標(biāo)int largest = -1;// 與左孩子判斷if(l<= n && A[l] > A[i])largest = l;elselargest = i;// 與右孩子判斷if(r <=n && A[r] > A[largest])largest = r;// i 結(jié)點(diǎn)不是最大值maxId 和i 結(jié)點(diǎn)進(jìn)行交換if(largest != i ){swap(A,largest,i); max_heapify(A,n,largest);}}/*** 交換* @param A* @param l* @param r*/public void swap(int [] A,int l,int r){int tmp = A[l];A[l] = A[r];A[r] = tmp;}// 父結(jié)點(diǎn)public int parent( int i){return i/2;}// 左孩子public int left(int i){return 2*i;}// 右孩子public int right(int i){return 2*i+1;} } public class heapSort {public static void main(String[] args) {heap h = new heap();// 第一個(gè)元素不考慮int[] A = new int[]{-1,4,1,3,2,16,9,10,14,8,7};int n = A.length-1;//h.build_max_heap(A, n);//printA(A);//16 14 10 8 7 9 3 2 4 1 //16 14 10 8 7 9 3 2 4 1 h.heap_sort(A, n);System.out.println();printA(A);}public static void printA(int[] A){for(int i:A){System.out.print(i+" ");}}} View Code?
總結(jié)
- 上一篇: SQL --运算符
- 下一篇: TCP/IP协议网络模型