內排序演算法-堆排序
阿新 • • 發佈:2019-01-25
堆排序,顧名思義,就是把待排序的資料按照一定的規則放到一個堆裡面去。不過,這裡這個堆不同於其他堆,這裡的堆是一顆完全二叉樹。那什麼是完全二叉樹呢,就是葉節點只能在最後一層或者倒數第二層,並且最後一層的結點都集中在該層最左邊的若干位置的二叉樹。
堆排序的基本思想就是構造一顆完全二叉樹,使得子節點的值均不大於(不小於)父節點,不大於對應大根堆,不小於對於小根堆。至於左右子節點的大小關係無所謂的啦。是不是有點抽象啦,咱們看圖講故事(此處我們使用大根堆)。
以陣列為例,左側是沒有加任何約束構造出來的完全二叉樹,右邊是按照大根堆的規則調整得到的完全二叉樹。
那如何將左側的完全二叉樹調整為右側的二叉樹呢?我們接著往下看。
按照從上往下的順序,依次比較每個父節點跟左右子節點的大小。當父節點的元素值小於左子節點的元素值或者右子節點的元素值時,將父節點的元素值與左右子節點較大的元素值進行交換。
程式碼實現如下:
void percDown(int a[], int n)
{
for(int i = 0; 2*i+1 < n; i++)
{
//存在左子節點
if(a[i] < a[2*i+1])
{
swap(&a[i],&a[2*i+1]);
}
//存在右子節點
if(a[i] < a[2*i+2] && 2*i+2 < n)
{
swap(&a[i],&a[2 *i+2]);
}
}
}
通過第一輪的調整,較小的元素已經沉到底部,最後一層節點的值均小於父節點。重複該過程次,為總節點的個數,即陣列的長度(深度為的完全二叉樹的節點個數為),直至每一層節點均小於父節點。
至此,陣列中的最大元素已經上浮至根節點。接下來,交換第一個元素跟最後一個元素的值,則陣列最後一個元素為當前最大的元素。(最後一個節點已經有序,後續討論不再包括該節點)
對於剩餘的個節點,由於剛剛的交換破壞了原有的最大堆,因此重複之前的二叉樹調整操作,得到新的最大堆。(此處的調整隻需進行一輪即可)
再次交換第一個節點和當前最後一個節點(即第個節點)的值,則倒數第二個節點歸位。
重複上述過程,直至第一個節點歸位。
程式碼實現如下:
void output(int a[], int n)
{
for(int i = 0; i < n; i++)
{
cout<<a[i]<<'\t';
}
cout<<endl;
}
void swap(int *x, int *y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
void heapSort(int a[], int n)
{
// 重複若干輪構造初始最大堆
for(int i = 0; i < log(n+1)/log(2)-1; i++)
{
percDown(a, n);
output(a,n);
}
// 依次將最大元素歸位,重新調整最大堆
for(int i = n-1; i >= 0; i--)
{
swap(a[0], a[i]);
percDown(a, i);
output(a,n);
}
}