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
}

posted @ 2022-04-21 11:41  fanzou  阅读(25)  评论(0编辑  收藏  举报