堆排序,顧名思義,是採用數據結構堆來進行排序的一種排序算法。
研究沒有規律的堆,沒有任何意義。特殊的堆有最大堆(父節點值大於等於左右字節點值),最小堆(父節點值小於等於子節點值)。一般採用最大堆來進行排序,圖1爲最大堆來表示一維數組。
圖1 最大堆表示一維數組
2叉樹堆的幾點特性
1、 最後父節點索引值
不妨設堆的總元素個數爲N;最後一個父節點的索引值Index = N/2 ;可以寫幾個簡單的堆進行數學歸納。圖1中的最後一個父節點5 = 10 /2 ;
這個特性主要是在採用自底向上構建堆的時候,循環起始值。
2、父節點與子節點索引值關係
LeftIndex = parent * 2;
RightIndex = parent * 2 + 1;
對於任意一個節點K,其父節點爲K/2,其左子節點爲2K,右子節點爲2K+1。
Max-Heapify(保持最大堆屬性)
散亂排布的堆對算法的實現非常不友好,沒有意義。因此,在隨機數據輸入時,需要對數據按照最大堆的屬性進行一個初始排序。對一個父節點數據的max-heapify形象的推導直接採用算法導論的圖,如圖2。節點2的數據顯然不符合最大堆的數據定義(父節點值不小於子節點值),因此把MAX(parent,left,right)替換到父節點,父節點的數據替換 到子節點中。對於替換的子節點4,仍然需要判斷其是否滿足最大堆數據定義,一直遞歸到滿足定義。
圖2 max-heapify原理
構建最大堆
對所有的父節點都進行max-heapify操作,自底向上從最後父節點一直循環到根節點,很巧妙。採用自底向上能夠保證遍歷過的數據始終是保持最大堆屬性的,max-heapify操作始終是求當前父節點下所有子節點的最大值。
那自頂向下會怎樣呢?
第一次max-heapify後,根節點並沒有變成最大值,還要再遍歷下根節點,這個顯然在算法設計上不合適。所以自底向上的構思很巧妙。
HeapSort
首先保證輸出數據滿足最大堆的數據屬性(第一行),然後把最大值提取出來存儲在數組末端;然後計算剩下數據的最大堆,把最大值提出來;循環操作到排序完成。採用算法導論圖解
編程實現
void CHeapSort::maxHeapify( std::vector<int> &arrayA, int index )
{
int l = leftChild(index);
int r = rightChild(index);
int maxValue = 0;
if(l < heapSize && arrayA[l] > arrayA[index])
{
maxValue = l;
}
else
{
maxValue = index;
}
if(r < heapSize && arrayA[r] > arrayA[maxValue])
{
maxValue = r;
}
/* 父節點部位最大值 */
if(maxValue != index)
{
std::swap(arrayA[index],arrayA[maxValue]);
maxHeapify(arrayA,maxValue);
}
}
void CHeapSort::buildMaxHeap( std::vector<int> &arrayA )
{
heapSize = arrayA.size();
int halfSize = heapSize >> 1;
for(int i = halfSize ;i >= 0; i--)
{
maxHeapify(arrayA,i);
}
}
void CHeapSort::heapSort( std::vector<int> &arrayA )
{
buildMaxHeap(arrayA);
for(int i = arrayA.size() - 1; i > 0; i--)
{
std::swap(arrayA[0],arrayA[i]);
heapSize--;
maxHeapify(arrayA,0);
}
}
結果: