(10)Go实现二叉堆-数组实现

二叉堆是树结构的一种,它满足以下性质:
(1) 堆中任意节点的值总是不大于(不小于)其子节点的值;
(2) 堆总是一棵完全树;
(3) 节点和节点之间应具有某种可比性
将任意节点不小于/不大于其子节点的堆叫做最大堆/最小堆,图示如下:
最小堆:最小值在顶端

最大堆:最大值在顶端
二叉堆索引之间关系

二叉堆中添加元素的思路,最大堆为例(最大值在最上面):
(1)将元素添加到堆最后,并根据其索引计算出父节点的索引;
(2)比较父节点和该节点,该节点大于父节点,则两者交换位置和索引;
(3)之后根据新索引计算出新的父节点索引,再进行下一轮比较,之后依此类推;

二叉堆中取出元素的思路,最大堆为例(最大值在最上面):
(1)二叉堆每次取出元素都只从root节点取值,这样每次取出的值都是该二叉堆中的最大值;
(2)将root节点取出后,用二叉堆中最后一个节点放到root节点原来的位置,之后做下沉操作;
(3)下沉思路:根据父节点索引计算出左右节点索引,再与左右节点对比,如果父节点小于左右节点中的最大值,则和左右节点中最大值交换位置,之后根据新父节点索引计算新左右节点索引,进行下一轮对比,之后依此类推;
(4)左右节点相等且大于父节点的情况下,优先选择和左节点做换位置;

最大堆的定义和实现:
// 最大堆
type maxHeap struct {
	size int
	nums []int
}

// 获取父节点索引
func parent(i int) int {
	if i == 0 {
		return 0
	}
	return (i - 1) / 2
}

// 获取左节点索引
func leftChild(i int) int {
	return 2*i + 1
}

func rightChild(i int) int {
	return 2*i + 2
}

func NewMaxHeap() *maxHeap {
	return &maxHeap{}
}

func (heap *maxHeap) GetSize() int {
	return heap.size
}

func (heap *maxHeap) IsEmpty() bool {
	return heap.size == 0
}

// 插入元素
func (heap *maxHeap) SiftUp(i int) {
	if heap.size < len(heap.nums) {
		heap.nums[heap.size] = i
	} else { // 扩容
		heap.nums = append(heap.nums, i)
	}
	parI := parent(heap.size)
	childI := heap.size
	for heap.nums[parI] < heap.nums[childI] {
		heap.nums[parI], heap.nums[childI] = heap.nums[childI], heap.nums[parI]
		childI = parI
		parI = parent(parI)
	}
	heap.size++
}

// 下沉操作
func siftDown(heap *maxHeap, parI int) {
	var maxI int
	for {
		leftI := leftChild(parI)
		switch {
		// 左索引超出size
		case leftI+1 > heap.size:
			return

			// 左索引不超,右索引超出size,说明左索引是最后索引
		case leftI+2 > heap.size:
			if heap.nums[parI] < heap.nums[leftI] {
				heap.nums[parI], heap.nums[leftI] = heap.nums[leftI], heap.nums[parI]
			}
			return
		case heap.nums[leftI] >= heap.nums[leftI+1]:
			maxI = leftI
		case heap.nums[leftI] < heap.nums[leftI+1]:
			maxI = leftI + 1
		}

		// 比左右子节点的值都大,返回
		if heap.nums[parI] >= heap.nums[maxI] {
			return
		}

		heap.nums[parI], heap.nums[maxI] = heap.nums[maxI], heap.nums[parI]
		parI = maxI
	}
}

// 取出对中最大元素,即root节点值
func (heap *maxHeap) SiftDown() (int, error) {
	if heap.IsEmpty() {
		return 0, errors.New("failed to siftDown,maxHeap is empty.")
	}
	vTop := heap.nums[0]
	heap.size--
	heap.nums[0], heap.nums[heap.size] = heap.nums[heap.size], 0

	siftDown(heap, 0)

	return vTop, nil
}

// 取出最大元素后,再放入一个新元素
func (heap *maxHeap) Replace(i int) (int, error) {
	if heap.IsEmpty() {
		return 0, errors.New("failed to siftDown,maxHeap is empty.")
	}
	vTop := heap.nums[0]
	heap.nums[0] = i

	siftDown(heap, 0)
	return vTop, nil
}

// 将数值arrs按照二叉堆的定义排序
func Heapify(arrs *[]int) *[]int {
	heap := &maxHeap{
		size: len(*arrs),
		nums: *arrs,
	}
	endParI := parent(heap.size - 1)
	for endParI >= 0 {
		siftDown(heap, endParI)
		endParI--
	}
	return &heap.nums
}

// 打印二叉堆
func (heap *maxHeap) Print() {
	str := ""
	count := 1
	j := 2
	k := 1
	for i := 0; i < heap.size; i++ {
		fmt.Println(str, heap.nums[i])
		count++
		if count == j {
			str = str + "--"
			j = 1 << uint(k)
			k++
			count = 0
		}
	}
}

// 查看堆中最大元素
func (heap *maxHeap) GetMax() (int, error) {
	if heap.IsEmpty() {
		return 0, errors.New("failed to get maxVal,maxHeap is empty.")
	}
	return heap.nums[0], nil
}
测试二叉树
for i := 0; i < 10; i++ {
		h.SiftUp(rand.Intn(20))
	}
	h.Print()

	for i := 0; i < 6; i++ {
		val, err := h.SiftDown()
		fmt.Printf("取出值:%d,  错误:%v\n", val, err)
	}
 19
-- 16
-- 18
---- 7
---- 1
---- 7
---- 5
------ 0
------ 1
------ 0
取出值:19,  错误:<nil>
取出值:18,  错误:<nil>
取出值:16,  错误:<nil>
取出值:7,  错误:<nil>
取出值:7,  错误:<nil>
取出值:5,  错误:<nil>

用二叉堆解决问题:
1)求n个数字中前m个最大值:https://www.jianshu.com/p/29493ea46f7b
2)求n个数字中前m个高频数字:https://www.jianshu.com/p/57918b843a2d

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值