堆排序與快速排序,歸併排序一樣都是時間複雜度為o(n*logn)的幾種常見排序方法。
堆排序是就地排序,輔助空間為o(1)。
它是不穩定的排序方法。(排序的穩定性是指如果在排序的序列中,存在前後相同的兩個元素的話,排序前 和排序後他們的相對位置不發生變化)
先說說什麼是堆,堆通常是乙個可以被看做一棵樹的陣列物件。滿足下列性質:
1.堆中某個節點的值總是不大於或不小於其父節點的值;
2.堆總是一棵完全樹(完全樹就是葉結點僅在層次最大的兩層出現的樹)。
將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。常見的堆有二叉堆、斐波那契堆等。由於其它幾種堆(二項式堆,斐波納契堆等)用的較少,一般將二叉堆就簡稱為堆。
堆的儲存
一般都用陣列來表示堆,i結點的父結點下標就為(i – 1) / 2。它的左右子結點下標分別為2 * i + 1和2 * i + 2。如第0個結點左右子結點下標分別為1和2。
堆的操作
建立堆:
一般情況下,樹並不滿足堆的條件,通過重新排列元素,可以建立一棵」堆化「的樹。如初始表:55 12 16,堆化後為:12 55 16。
堆的插入:
每次插入都是將新資料放在陣列最後。然後樹被更新以恢復堆次序。如初始表:12 22 7 ,插入新資料後,陣列為12 22 7 16,然後重排樹的順序,陣列為12 16 7 22。
可以發現從這個新資料的父結點到根結點必然為乙個有序的數列。
// 新加入i結點 其父結點為(i - 1) / 2
void minheapfixup(int a, int i)
a[i] = temp;
}
//在最小堆中加入新的資料nnum
void minheapaddnumber(int a, int n, int nnum)
堆的刪除
堆中每次都只能刪除第0個資料。為了便於重建堆,實際的操作是將最後乙個資料的值賦給根結點,然後再從根結點開始進行一次從上向下的調整。調整時先在左右兒子結點中找最小的,如果父結點比這個最小的子結點還**明不需要調整了,反之將父結點和它交換後再考慮後面的結點。相當於從根結點將乙個資料的「下沉」過程。
如初始表:12 16 50 22,刪除第0個資料後,陣列為22 16 50 _,然後重排樹的順序,陣列為16 22 50。
// 從i節點開始調整,n為節點總數 從0開始計算 i節點的子節點為 2*i+1, 2*i+2
void minheapfixdown(int a, int i, int n)
a[i] = temp;
}//在最小堆中刪除數
void minheapdeletenumber(int a, int n)
堆化陣列
關於怎樣把乙個資料進行堆化。可能很多人會想,要乙個乙個的從陣列中取出資料來建立堆?不用。
比如說:int a[0] = ;
如果把這個陣列看成是一棵樹,那麼它的葉子結點19,59,64,3,18都分別是乙個合法的堆。只要把49開始向下調整就可以了。然後再取29,16,11,9結點分別作一次向下調整操作就可以了。
//建立最小堆
void makeminheap(int a, int n)
就這樣,堆的操作就全部完成了。
說了這麼多,終於到主角登場了。
根據堆的性質,堆建好之後。堆中第0個資料是堆中最小的資料。取出這個資料再執行下堆的刪除操作。這樣堆中第0個資料又是堆中最小的資料,重複上述步驟直至堆中只有乙個資料時就直接取出這個資料。
由於堆也是用陣列模擬的,故堆化陣列後,第一次將a[0]與a[n - 1]交換,再對a[0…n-2]重新恢復堆。第二次將a[0]與a[n – 2]交換,再對a[0…n - 3]重新恢復堆,重複這樣的操作直到a[0]與a[1]交換。由於每次都是將最小的資料併入到後面的有序區間,故操作完成後整個陣列就有序了。
void minheapsorttodescendarray(int a, int n)
}
注意使用最小堆排序後是遞減陣列,要得到遞增陣列,可以使用最大堆。
應用:堆是一種經典的資料結構,向堆中插入、刪除元素時間複雜度都是 o(lgn), n 為堆中元素的個數,而獲取最小 key 值(小根堆)的複雜度為 o(1)。
libevent中的定時事件管理就是用乙個以時間作為 key 的小根堆結構做的,放棄了原來的紅黑樹,大概就是堆比紅黑樹簡單吧。
參考:
堆與堆排序
二叉堆是完全二叉樹或者是近似完全二叉樹。二叉堆滿足二個特性 1 父結點的鍵值總是大於或等於 小於或等於 任何乙個子節點的鍵值。2 每個結點的左子樹和右子樹都是乙個二叉堆 都是最大堆或最小堆 當父結點的鍵值總是大於或等於任何乙個子節點的鍵值時為最大堆。當父結點的鍵值總是小於或等於任何乙個子節點的鍵值時...
堆與堆排序
堆排序與快速排序,歸併排序一樣都是時間複雜度為o n logn 的幾種常見排序方法。學習堆排序前,先講解下什麼是資料結構中的二叉堆。二叉堆是完全二叉樹或者是近似完全二叉樹。二叉堆滿足二個特性 1 父結點的鍵值總是大於或等於 小於或等於 任何乙個子節點的鍵值。2 每個結點的左子樹和右子樹都是乙個二叉堆...
堆與堆排序
堆的儲存 一般都用陣列來表示堆,i結點的父結點下標就為 i 1 2。它的左右子結點下標分別為2 i 1和2 i 2。如第0個結點左右子結點下標分別為1和2。堆排序的思想 利用大頂堆 小頂堆 堆頂記錄的是最大關鍵字 最小關鍵字 這一特性,使得每次從無序中選擇最大記錄 最小記錄 變得簡單。其基本思想為 ...