這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
關於堆排序的演算法,可以參考我去年的文章《堆排序(HEAP SORT)》。那篇文章講的是建立小頂堆進行的排序,這裡說的是建立大頂堆建立的排序,差不多。
在Golang源碼的sort包裡,內建了排序函數。該函數可以對各種類型進行排序,只不過該類型需要實現三個函數,使得該類能夠實現Interface介面。
type Interface interface {// Len is the number of elements in the collection.Len() int// Less reports whether the element with// index i should sort before the element with index j.Less(i, j int) bool// Swap swaps the elements with indexes i and j.Swap(i, j int)}
這三個函數分別是,擷取排序隊列長度、隊列任意兩個元素比較大小和交換任意兩個元素。
func (a BySortIndex) Len() int { return len(a) }func (a BySortIndex) Swap(i, j int) { a[i], a[j] = a[j], a[i] }func (a BySortIndex) Less(i, j int) bool {return a[i] < a[j]}
如果是整形數組,可以像上面這樣實現。Golang支援多值賦值,所以交換值很簡單。內建的len也使得長度遍曆很簡單。比較大小,可以根據實際情況自己定義。
堆排序的核心就是建立大頂堆和交換值,它是本地排序,不需要新分配空間。Golang的源碼我已經加了注釋,也不難,大家直接閱讀即可。
func heapSort(data Interface, a, b int) {first := alo := 0hi := b - a// Build heap with greatest element at top.for i := (hi - 1) / 2; i >= 0; i-- {siftDown(data, i, hi, first)}// Pop elements, largest first, into end of data.// 二叉樹結構當中最後一個有子結點的結點for i := hi - 1; i >= 0; i-- {data.Swap(first, first+i)siftDown(data, lo, i, first)}}// 建立樹函數// 父節點root的左孩子2*root + 1func siftDown(data Interface, lo, hi, first int) {root := lofor {child := 2*root + 1if child >= hi { // child 超出了數組長度,也就是該結點無孩子結點,返回break}if child+1 < hi && data.Less(first+child, first+child+1) { // 右孩子結點存在child++}// 以上是為了在孩子結點當中找到較大的結點,用child表示if !data.Less(first+root, first+child) {return}// 如果根結點小於較大的孩子結點,則進行交換;該孩子結點的堆結構可能會被影響,繼續去處理孩子結點data.Swap(first+root, first+child)root = child}}
本文所涉及到的完整源碼請參考。
參考文獻
- 【1】堆排序(HEAP SORT) - Cyeam
原文連結:Go語言的堆排序實現,轉載請註明來源!