go使用标准库实现堆,及前K个高频元素,滑动窗口的最大值解法

0.前言

堆是一种树形数据结构(完全二叉树),分为大顶堆和小顶堆,顾名思义,大顶堆就是堆顶(第一个元素)始终存放的是这组元素中的最大元素,小顶堆就是堆顶元素是最小元素。如果需要从一组对象中查找最大值最小值,且元素会动态增加,使用堆能够高效率的完成需求。GO中标准库提供了heap的功能,一起看看如何使用吧。

1.GO语言中的Heap包使用方法

使用Heap,主要分为三步:

  • 定义堆结构体
  • 实现heap中Interface接口
  • 初始化堆并使用堆

直接上代码:

package main

import (
	"container/heap"
	"fmt"
)
//定义堆结构体
type HeapInts []int

// 实现Interface接口
// 其中包含 sort中的Interface接口 和push、pop接口
// 注意 push和pop接口的接收者必须是指针
func (h HeapInts) Len() int {
    
    
	return len(h)
}
//此处是大顶堆,如果是 < 即变成小顶堆
func (h HeapInts) Less(i, j int) bool {
    
    
	return h[i] > h[j]
}

func (h HeapInts) Swap(i, j int) {
    
    
	h[i],h[j]=h[j],h[i]
}

func (h *HeapInts) Push(x interface{
    
    }) {
    
    
	*h =append(*h,x.(int))
}

func (h *HeapInts) Pop() interface{
    
    } {
    
    
	temp:=(*h)[len(*h)-1]
	*h=(*h)[:len(*h)-1]
	return temp
}
func main()  {
    
    
	var h HeapInts
	h=append(h,2,1,3)
	fmt.Println(h)
    //初始化堆
	heap.Init(&h)
	fmt.Println(h)
    //向堆中插入元素
	heap.Push(&h,9)
	heap.Push(&h,5)
	fmt.Println(h)
    //弹出堆顶元素
	x:=heap.Pop(&h)
	fmt.Println(x)
	fmt.Println(h)
}

2.一些使用堆解决的算法题

leetcode上有很多可以用堆解决的题目,这里放出两题:

347. 前 K 个高频元素 - 力扣(LeetCode)

239. 滑动窗口最大值 - 力扣(LeetCode)

2.1 前K个高频元素

在本题中,堆中每个节点存储一个数的值和数的出现次数,出现次数用于排序,值用于得到答案。

首先使用map记录所有元素的出现次数,然后根据上述的构建堆的步骤构建堆,用于存放map中的数据,此处构建的是大顶堆,最后Pop出K个元素,即是答案,代码如下:

type mm  struct {
    
    
	k int
	v int
}
type ms []mm

func (m ms) Len() int {
    
    
	return len(m)
}
//根据v值进行排序
func (m ms) Less(i, j int) bool {
    
    
	return m[i].v > m[j].v
}

func (m ms) Swap(i, j int) {
    
    
	m[i],m[j]=m[j],m[i]
}

func (m *ms) Push(x interface{
    
    }) {
    
    
	 *m=append(*m,x.(mm))
}

func (m *ms) Pop() interface{
    
    } {
    
    
	temp:= (*m)[len(*m)-1]
	*m=(*m)[:len(*m)-1]
	return temp
}

func topKFrequent(nums []int, k int) []int {
    
    
	var result []int
	m:=make(map[int]int)
	for _,v:=range nums{
    
    
		m[v]++
	}

	var h ms
	for k,v:=range m{
    
    
		h=append(h,mm{
    
    k: k,v: v})
	}
    //初始化堆
	heap.Init(&h)
    //得到答案
	for i:=0;i<k;i++{
    
    
		result=append(result,heap.Pop(&h).(mm).k)
	}
	return result
}

2.2 滑动窗口的最大值

在本题中,堆的数据结构中,每个节点存储一个数的下标和数的值。值用于排序,下标用于判断是否在滑动窗口之中。具体流程如下:

  • 首先使用第一个滑动窗口的值来构建堆。
  • 往右移动一格窗口,将新加入的节点入堆。然后对堆顶元素进行检测(使用k),如果此元素处于滑动窗口中,保存到结果,继续移动窗口;如果不处于窗口中,弹出元素,检测下一个堆顶元素,直到找到一个堆中元素。
  • 重复第二步直到遍历完整个数组,返回结果

代码如下:

type mm  struct {
    
    
	k int
	v int
}
type ms []mm

func (m ms) Len() int {
    
    
	return len(m)
}

func (m ms) Less(i, j int) bool {
    
    
	return m[i].v > m[j].v
}

func (m ms) Swap(i, j int) {
    
    
	m[i],m[j]=m[j],m[i]
}

func (m *ms) Push(x interface{
    
    }) {
    
    
	 *m=append(*m,x.(mm))
}

func (m *ms) Pop() interface{
    
    } {
    
    
	temp:= (*m)[len(*m)-1]
	*m=(*m)[:len(*m)-1]
	return temp
}

func maxSlidingWindow(nums []int, k int) []int {
    
    
	var h ms
	var result []int
	for i := 0; i < k; i++ {
    
    
		h=append(h,mm{
    
    k: i,v: nums[i]})
	}
	heap.Init(&h)
	result=append(result,h[0].v)
	for i := k; i < len(nums); i++ {
    
    
		heap.Push(&h, mm{
    
    k: i, v: nums[i]})
		for {
    
    
			if h[0].k >= i - k + 1 {
    
    
				result = append(result, h[0].v)
				break
			}else{
    
    
				heap.Pop(&h)
			}
		}
	}
	return result
}

Guess you like

Origin blog.csdn.net/doreen211/article/details/125812183