1. 程式人生 > 其它 >go 堆排序

go 堆排序

package main

import (
	"fmt"
)

func main() {
	heap := BuildHeap([]int{33, 24, 8, 3, 1001, 15, 16, 15, 30, 17, 19})
	var sortedArr []int
	for  {
		v,ok := heap.Pop()
		if !ok {
			break
		}
		sortedArr = append(sortedArr, v)
	}

	fmt.Println(sortedArr)
}

// 小頂堆(完全二叉樹,最下面一層的節點都集中在該層最左邊的連續位置上;父節點小於子節點;所以可以用陣列存放)
// 儲存結構:i 為下標, 左子節點 i*2+1, 右子節點 i*2+2, 父節點 (i-1)/2

type Heap []int

// 交換位置
func (h Heap) swap(i, j int) {
	h[j], h[i] = h[i], h[j]
}

// 比較節點大小
func (h Heap) less(i, j int) bool {
	return h[i] < h[j]
}

// 插入
// 首先插入最末尾節點,自下而上與父節點比較,不斷上升,一直到滿足小頂堆規則
// 兩種情況:1.一直升到堆頂;2.到某一位置時發現父節點比自己小,則停止。
func (h Heap) up(i int) {
	for {

		f := (i - 1) / 2

		// 停止
		if i == f || h.less(f, i) {
			break
		}

		// 上升
		h.swap(f, i)
		i = f
	}
}

func (h *Heap) Push(i int) {
	*h = append(*h, i)
	h.up(len(*h) - 1)
}

// 刪除
// 1.將末節點和刪除節點交換,然後刪除末尾節點
// 2.原末端節點需要與新位置上的父節點做比較,如果小於要做 up(看上面的方法),
// 如果大於父節點,則再和子節點做比較,即 down 操作,直到該節點下降到小於最小子節點為止。與最小的子節點交換
func (h Heap) down(i int) {
	for {
		lson := i*2 + 1
		rson := i*2 + 2

		if rson >= len(h) {
			break
		}

		if h.less(i, lson) && h.less(i, rson) {
			break
		}
		if h.less(lson, rson) {
			h.swap(i, lson)
			i = lson
		} else {
			h.swap(i, rson)
			i = rson
		}

	}
}

func (h *Heap) Remove(i int) (int, bool) {
	if i < 0 || i > len(*h)-1 {
		return 0, false
	}
	// 交換
	tail := len(*h) - 1
	h.swap(tail, i)
	// 刪除最後元素
	x := (*h)[tail]
	*h = (*h)[:tail]

	// i節點下降或者上升
	if i == 0 || (*h)[i] > (*h)[(i-1)/2] {
		h.down(i)
	} else {
		h.up(i)
	}
	return x, true
}

// 彈出頂點
func (h *Heap) Pop() (int, bool) {
	return h.Remove(0)
}

// 建堆
// 自底向上調整,不斷的將最小值向上推。倒數第二層開始,從右到左
func BuildHeap(arr []int) Heap {
	h := Heap(arr)
	last := len(arr) - 1
	// 最後節點的父節點
	for i := (last - 1) / 2; i >= 0; i-- {
		h.down(i)
	}
	return h
}