在軟體設計相關領域,“堆(Heap)”的概念主要涉及到兩個方面:
一種是資料結構,邏輯上是一顆完全二叉樹,儲存上是一個數組對象(二元堆積)。
另一種是垃圾收集儲存區,是軟體系統可以編程的記憶體地區。
本文所說的堆指的是前者,另外,這篇文章中堆中元素的值均以整形為例
堆排序的時間複雜度是O(nlog2n),與快速排序達到相同的時間複雜度. 但是在實際應用中,我們往往採用快速排序而不是堆排序. 這是因為快速排序的一個好的實現,往往比堆排序具有更好的表現. 堆排序的主要用途,是在形成和處理優先順序隊列方面. 另外, 如果計算要求是類優先順序隊列(比如, 只要返回最大或者最小元素, 只有有限的插入要求等), 堆同樣是很適合的資料結構.
堆排序
堆排序是一種選擇排序。是不穩定的排序方法。時間複雜度為O(nlog2n)。
堆排序的特點是:在排序過程中,將排序數組看成是一棵完全二叉樹的順序儲存結構,利用完全二叉樹中雙親節點和孩子節點之間的內在關係,在當前無序區中選擇關鍵字最大(或最小)的記錄。
基本思想
1.將要排序的數組建立為一個大根堆。大根堆的堆頂元素就是這個堆中最大的元素。
2.將大根堆的堆頂元素和無序區最後一個元素交換,並將無序區最後一個位置例入有序區,然後將新的無序區調整為大根堆。
重複操作,無序區在遞減,有序區在遞增。
初始時,整個數組為無序區,第一次交換後無序區減一,有序區增一。
每一次交換,都是大根堆的堆頂元素插入有序區,所以有序區保持是有序的。
大根堆和小根堆
堆:是一顆完全二叉樹。
大根堆:所有節點的子節點比其自身小的堆
小根堆:所有節點的子節點比其自身大的堆
堆與數組的關係
堆是一種邏輯結構(形象的表示資料的儲存格式),數組則是資料的實際儲存結構(對應資料的儲存地址),堆中的根節點與左右子節點在儲存數組中的位置關係如下:假設根節點在數組中的位置(數組下標)為 i ,那麼左節點在數組中的位置(數組下標)為 i * 2 + 1 , 右節點在數組中的位置(數組下標)為 i * 2 + 2 。
以上是基本的知識點,具體代碼如下所示:
//堆排序演算法(傳遞待排數組名,即:數組的地址。故形參數組的各種操作反應到實參數組上)
private static void HeapSortFunction(int[] array)
{
try
{
BuildMaxHeap(array); //建立大頂推(初始狀態看做:整體無序)
for (int i = array.Length - 1; i > 0; i--)
{
Swap(ref array[0], ref array[i]); //將堆頂元素依次與無序區的最後一位交換(使堆頂元素進入有序區)
MaxHeapify(array, 0, i); //重新將無序區調整為大頂堆
}
}
catch (Exception ex)
{ }
}
/// <summary>
/// 建立大頂推(根節點大於左右子節點)
/// </summary>
/// <param name="array">待排數組</param>
private static void BuildMaxHeap(int[] array)
{
try
{
//根據大頂堆的性質可知:數組的前半段的元素為根節點,其餘元素都為分葉節點
for (int i = array.Length / 2 - 1; i >= 0; i--) //從最底層的最後一個根節點開始進行大頂推的調整
{
MaxHeapify(array, i, array.Length); //調整大頂堆
}
}
catch (Exception ex)
{ }
}
/// <summary>
/// 大頂推的調整過程
/// </summary>
/// <param name="array">待調整的數組</param>
/// <param name="currentIndex">待調整元素在數組中的位置(即:根節點)</param>
/// <param name="heapSize">堆中所有元素的個數</param>
private static void MaxHeapify(int[] array, int currentIndex, int heapSize)
{
try
{
int left = 2 * currentIndex + 1; //左子節點在數組中的位置
int right = 2 * currentIndex + 2; //右子節點在數組中的位置
int large = currentIndex; //記錄此根節點、左子節點、右子節點 三者中最大值的位置
if (left < heapSize && array[left] > array[large]) //與左子節點進行比較
{
large = left;
}
if (right < heapSize && array[right] > array[large]) //與右子節點進行比較
{
large = right;
}
if (currentIndex != large) //如果 currentIndex != large 則表明 large 發生變化(即:左右子節點中有大於根節點的情況)
{
Swap(ref array[currentIndex], ref array[large]); //將左右節點中的大者與根節點進行交換(即:實現局部大頂堆)
MaxHeapify(array, large, heapSize); //以上次調整動作的large位置(為此次調整的根節點位置),進行遞迴調整
}
}
catch (Exception ex)
{ }
}
/// <summary>
/// 交換函數
/// </summary>
/// <param name="a">元素a</param>
/// <param name="b">元素b</param>
private static void Swap(ref int a, ref int b)
{
int temp = 0;
temp = a;
a = b;
b = temp;
}