最大堆進階:堆排序及其優化
上一講中我們把最大堆的基本儲存和兩個經典的操作進行了介紹,並且在文章的最後,我們依次執行了刪除根節點的操作,這時候你看到了一個排好序的數列,本節課我就把堆排序給您講清楚。
下面的圖片是ShiftUp和ShiftDown的原始碼,一是你可以驗證一下自己編寫的是不是合理,再一個也是作為我們後面文字的輔助理解材料。

ShifUp和ShiftDown原始碼
基礎堆排序
基礎堆排序的原始碼如下:

基礎堆排序原始碼
從原始碼中可以看出來,基礎堆排序的過程非常簡單:先定義一個最大堆,然後將待排序陣列中的元素向最大堆中依次插入(構造最大堆的過程),最後再把最大堆中的元素一個一個取出來(每次操作都是取出根節點),從後向前依次放回待排序陣列中。
下面是insertItem(插入操作)和extractItem(刪除根節點操作)的原始碼:

insertItem和extractItem的原始碼
基礎堆排序有兩個問題:
一是:構建最大堆的過程比較繁瑣,要對每一個插入最大堆的元素進行shiftUp操作;
二是:使用了額外的儲存空間,構建的最大對物件就是額外的儲存空間。
下面我們先解決第一個問題。
最大堆的heapify
heapify操作的定義如下:
我們可以把任何一個數列看成是一個帶重組的最大堆,而這個數列中所有的葉子節點其實單獨拿出來都是一個最大堆。那我們就從最後一個非葉子開始依次往前,對每一個非葉子節點做shiftDown操作,操作完成之後,這個數列也就變成了一個最大堆。
原始碼如下圖所示:

heapify原始碼
結合heapify操作,我們可以重寫堆排序的原始碼,原始碼如下:

結合heapify操作的堆排序
怎麼樣,看來上面兩個排序的原始碼有什麼感想呢?我在學習這部分演算法時就有一種感覺:
過程只要想清楚,原始碼寫出來那是即精悍,又短小。
優化的堆排序
下面我們解決基礎堆排序中的第二個問題,即處理掉那額外的儲存空間。演算法過程如下:
對於一個待排序的陣列,我們第一步要做的就是把這個陣列通過heapify的過程先整理成最大堆。然後,在這個陣列中,對最大堆進行整理,形成一個排好序的數列,如何整理呢?詳細過程如下:

最大堆的排序過程
最大堆優化後排序的原始碼如下:

優化後的堆排序原始碼
怎麼樣,是不是還是那個感覺:即短小,又精悍。
好,到此為止我們已經把最基本的四種排序(插入排序、歸併排序、快速排序、堆排序)都進行了介紹,下一篇文章我會把所有已經講過的排序做一個總結,希望能夠對你理解排序演算法有幫助。
我是徐建航, 這是我寫的第63篇文章,歡迎你加入007社群,七天寫一篇,一起寫七年,七年之後一起去南極。
