GO--堆(have TODO)

news2024/12/23 1:53:54

堆(Heap)是一种特殊的数据结构。它是一棵完全二叉树(完全二叉树是指除了最后一层外,每一层上的节点数都是满的,并且最后一层的节点都集中在左边),结放在数组(切片)中,通常分为最大堆(Max Heap)和最小堆(Min Heap)两种类型。

  • 最大堆:在最大堆中,对于每个非叶子节点,它的值都大于或等于其左右子节点的值。也就是说,根节点的值是整个堆中的最大值。例如,对于节点i,如果它有左子节点2i + 1和右子节点2i+2(这里假设节点编号从 0 开始),那么heap[i]>=heap[2i + 1]heap[i]>=heap[2i+2]
  • 最小堆:与最大堆相反,在最小堆中,对于每个非叶子节点,它的值都小于或等于其左右子节点的值。根节点的值是整个堆中的最小值。即对于节点iheap[i]<=heap[2i + 1]heap[i]<=heap[2i+2]

堆的实现

1、使用container/heap包

heap源码中定义了一个Interface 的接口,此接口一共包含五个方法,我们定义一个实现此接口的类就实现了一个二叉堆

package main

import (
	"container/heap"
	"fmt"
)

type MaxHeap []int

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

func (m MaxHeap) Less(i, j int) bool {
	//建立大根堆,使用>
	return m[i] > m[j]
}

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

func (m *MaxHeap) Push(x any) {
	*m = append(*m, x.(int))
}

func (m *MaxHeap) Pop() any {
	//拿到堆顶元素
	res := (*m)[len(*m)-1]
	//删除堆顶元素
	*m = (*m)[:len(*m)-1]
	return res
}

func main() {
	h := make(MaxHeap, 0)
    //结构体实现了接口中的全部方法后,结构体也就是这个接口类型了,因此我们可以传入h
	heap.Init(&h)

	heap.Push(&h, 2)
	heap.Push(&h, 1)
	heap.Push(&h, 3)

	fmt.Println(heap.Pop(&h))
	fmt.Println(heap.Pop(&h))
	fmt.Println(heap.Pop(&h))

}

下面我们一起去看看源码:

 

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package heap provides heap operations for any type that implements
// heap.Interface. A heap is a tree with the property that each node is the
// minimum-valued node in its subtree.
//
// The minimum element in the tree is the root, at index 0.
//
// A heap is a common way to implement a priority queue. To build a priority
// queue, implement the Heap interface with the (negative) priority as the
// ordering for the Less method, so Push adds items while Pop removes the
// highest-priority item from the queue. The Examples include such an
// implementation; the file example_pq_test.go has the complete source.
package heap

import "sort"

// The Interface type describes the requirements
// for a type using the routines in this package.
// Any type that implements it may be used as a
// min-heap with the following invariants (established after
// [Init] has been called or if the data is empty or sorted):
//
//	!h.Less(j, i) for 0 <= i < h.Len() and 2*i+1 <= j <= 2*i+2 and j < h.Len()
//
// Note that [Push] and [Pop] in this interface are for package heap's
// implementation to call. To add and remove things from the heap,
// use [heap.Push] and [heap.Pop].


堆的接口,定义实现该接口内部的全部方法后,我们定义的类就实现了堆
下面是sort.Interface的实现,内部有三个方法,分别是Len()、Less()、Swap()
通过Less()方法,以此确定建立的是大根堆,还是小根堆

// An implementation of Interface can be sorted by the routines in this package.
// The methods refer to elements of the underlying collection by integer index.
type Interface interface {
	// Len is the number of elements in the collection.
	Len() int

	// Less reports whether the element with index i
	// must sort before the element with index j.
	//
	// If both Less(i, j) and Less(j, i) are false,
	// then the elements at index i and j are considered equal.
	// Sort may place equal elements in any order in the final result,
	// while Stable preserves the original input order of equal elements.
	//
	// Less must describe a transitive ordering:
	//  - if both Less(i, j) and Less(j, k) are true, then Less(i, k) must be true as well.
	//  - if both Less(i, j) and Less(j, k) are false, then Less(i, k) must be false as well.
	//
	// Note that floating-point comparison (the < operator on float32 or float64 values)
	// is not a transitive ordering when not-a-number (NaN) values are involved.
	// See Float64Slice.Less for a correct implementation for floating-point values.
	Less(i, j int) bool

	// Swap swaps the elements with indexes i and j.
	Swap(i, j int)
}


^
|
|
| 
重点

type Interface interface {
	sort.Interface
	Push(x any) // add x as element Len()
	Pop() any   // remove and return element Len() - 1.
}

|
|
|
——


init方法,将实现接口的类init进行建堆

// Init establishes the heap invariants required by the other routines in this package.
// Init is idempotent with respect to the heap invariants
// and may be called whenever the heap invariants may have been invalidated.
// The complexity is O(n) where n = h.Len().
func Init(h Interface) {
	// heapify
	n := h.Len()
	for i := n/2 - 1; i >= 0; i-- {
		down(h, i, n)
	}
}


Push插入元素

// Push pushes the element x onto the heap.
// The complexity is O(log n) where n = h.Len().
func Push(h Interface, x any) {
	h.Push(x)
	up(h, h.Len()-1)
}

Pop弹出元素

// Pop removes and returns the minimum element (according to Less) from the heap.
// The complexity is O(log n) where n = h.Len().
// Pop is equivalent to [Remove](h, 0).
func Pop(h Interface) any {
	n := h.Len() - 1
	h.Swap(0, n)
	down(h, 0, n)
	return h.Pop()
}


Remove移除元素

// Remove removes and returns the element at index i from the heap.
// The complexity is O(log n) where n = h.Len().
func Remove(h Interface, i int) any {
	n := h.Len() - 1
	if n != i {
		h.Swap(i, n)
		if !down(h, i, n) {
			up(h, i)
		}
	}
	return h.Pop()
}


Fix重新调整堆

// Fix re-establishes the heap ordering after the element at index i has changed its value.
// Changing the value of the element at index i and then calling Fix is equivalent to,
// but less expensive than, calling [Remove](h, i) followed by a Push of the new value.
// The complexity is O(log n) where n = h.Len().
func Fix(h Interface, i int) {
	if !down(h, i, h.Len()) {
		up(h, i)
	}
}


堆调整的两个重要方法,up和down,具体我没看,我们可以自己写出更简单更好理解的heapInsert和heapify

func up(h Interface, j int) {
	for {
		i := (j - 1) / 2 // parent
		if i == j || !h.Less(j, i) {
			break
		}
		h.Swap(i, j)
		j = i
	}
}

func down(h Interface, i0, n int) bool {
	i := i0
	for {
		j1 := 2*i + 1
		if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
			break
		}
		j := j1 // left child
		if j2 := j1 + 1; j2 < n && h.Less(j2, j1) {
			j = j2 // = 2*i + 2  // right child
		}
		if !h.Less(j, i) {
			break
		}
		h.Swap(i, j)
		i = j
	}
	return i > i0
}

2、自己实现堆

自己实现堆,最重要的就是heapInsert和heapify方法,通过这两个方法,以此保证正确实现堆结构。

heapInsert方法:新来的一个元素,新来的元素若大于他的父节点元素,则上升,确定其应该在堆中的正确位置,实现大根堆

for循环虽然只有一个判断,却包含了另一层判断,当index到达0位置后,循环也会停止

func heapInsert(arr []int, index int) {
	for arr[index] > arr[(index-1)/2] {
		arr[index], arr[(index-1)/2] = arr[(index-1)/2], arr[index]
		index = (index - 1) / 2
	}
}

heapify方法,节点位置为最大值,实现大根堆

func heapify(arr []int, index int, heapsize int) {
	//左孩子的位置
	left := index*2 + 1
	for left < heapsize {
		//largest的含义为:节点和子节点中的最大值
		largest := left 	//一开始放在左孩子上
		//如果存在右孩子,并且右孩子的值比左孩子大,以此选出左右孩子中的较大节点
		if left+1 < heapsize && arr[left] < arr[left+1] {
			//在largest放在右孩子的位置
			largest = left + 1
		}
		//判断largest和index节点位置谁大
		if arr[index] > arr[largest] {
			//若节点大于largest,则largest来到index位置
			largest = index
		}
		//index位置为最大,则退出循环,不需要向下进行
		if largest == index {
			break
		}
		//交换节点位置
		arr[largest], arr[index] = arr[index], arr[largest]
		//index来到largest位置,继续下次循环
		index = largest
		//left继续来到左孩子的位置
		left = index * 2 + 1
	}
}

建堆

// 建堆
func buildHeap(arr []int) {
	//从上至下建堆
	//for i := 0; i < len(arr); i++ {
	//	heapInsert(arr, i)
	//}

	//从下至上建堆
	for i := len(arr) - 1; i >= 0; i-- {
		heapify(arr, i, len(arr))
	}

}

堆排序

能够自己实现堆之后,实现堆排序就十分简单了。

若我们实现了大根堆,那么每次堆顶元素就是最大值,那么只需要堆顶与最后一个元素进行交换,交换后,堆的大小减1,然后在将交换后位于第一位的元素使用heapify让其下降,这样循环进行,最后堆的大小减到0,那么就实现了排序。

func heapSort(arr []int) {
	heapSize := len(arr)
	heapSize--
	arr[0], arr[heapSize] = arr[heapSize], arr[0]
	for heapSize > 0 {
		heapify(arr, 0, heapSize)
		heapSize--
		arr[0], arr[heapSize] = arr[heapSize], arr[0]
	}
}

Leetcode堆相关题目

(累了,下次再写)

1、215. 数组中的第K个最大元素 - 力扣(LeetCode)

求数组中第K大的元素,有很简单的方法,将数组进行排序,返回第k个数,即为第k大的数。

题目要求时间复杂度为O(N),可以使用桶排序,这道题目给了数据范围,确实可以用。

题解里还有改进的快速排序,也能达到O(N)的时间复杂度。

但我们主要还是使用堆来完成。

//题解标准答案
func findKthLargest(nums []int, k int) int {
	//建立大根堆
	buildHeap(nums)
	heapSize := len(nums)
	for i := len(nums) - 1; i >= len(nums)-k+1; i-- {
		nums[0], nums[i] = nums[i], nums[0]
		heapSize--
		heapify(nums, 0, heapSize)
	}

	return nums[0]
}

//我觉得不好理解,就自己写了i从0——K,但是需要加判断,要不会出现nums[-1]的报错
func findKthLargest(nums []int, K int) int {
	buildHeap(nums)
	heapSize := len(nums)
	nums[0], nums[heapSize-1] = nums[heapSize-1], nums[0]
	for i:= 0; i < K; i++ {
		heapSize--
		heapify(nums,0,heapSize)
		if heapSize-1 >= 0{
			nums[0], nums[heapSize-1] = nums[heapSize-1], nums[0]
		} else {
			return nums[heapSize]
		}
	}
	return nums[heapSize]
}


func heapify(arr []int, index int, heapsize int) {
	//左孩子的位置
	left := index*2 + 1
	for left < heapsize {
		//largest的含义为:节点和子节点中的最大值
		largest := left //一开始放在左孩子上
		//如果存在右孩子,并且右孩子的值比左孩子大,以此选出左右孩子中的较大节点
		if left+1 < heapsize && arr[left] < arr[left+1] {
			//在largest放在右孩子的位置
			largest = left + 1
		}
		//判断largest和index节点位置谁大
		if arr[index] > arr[largest] {
			//若节点大于largest,则largest来到index位置
			largest = index
		}
		//index位置为最大,则退出循环,不需要向下进行
		if largest == index {
			break
		}
		//交换节点位置
		arr[largest], arr[index] = arr[index], arr[largest]
		//index来到largest位置,继续下次循环
		index = largest
		//left继续来到左孩子的位置
		left = index*2 + 1
	}
}


func buildHeap(arr []int) {
	//从上至下建堆
	//for i := 0; i < len(arr); i++ {
	//	heapInsert(arr, i)
	//}

	//从下至上建堆
	for i := len(arr) - 1; i >= 0; i-- {
		heapify(arr, i, len(arr))
	}

}

2、502. IPO - 力扣(LeetCode)

3、373. 查找和最小的 K 对数字 - 力扣(LeetCode)

TODO

4、295. 数据流的中位数 - 力扣(LeetCode)

TODO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2263989.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Yolov11学习笔记

YOLOv11学习笔记 1.conda环境变量配置 1.1pycharm中新建conda虚拟环境 在win11系统中&#xff0c;创建的conda虚拟环境不会在conda的安装目录下&#xff0c; 而是会在C:\Users\Admin.conda\envs 比如我的anaconda安装目录在F:\anaconda3 但是生成的虚拟环境在C:\Users\Adm…

算法 计算大的长方形容器中,存放一排小长形容器,计算出小长形容器中最后一个元素的x坐标的位置的实现方法

1、先上个图&#xff1a; 2、说明 1&#xff09;中间的蓝色长方形是里面的橙色长方形的容器&#xff0c;比如第一个图中width2width3&#xff0c;因为只有一个&#xff0c;第二个图中有二个小的长方形&#xff0c;也就是说width22width3&#xff0c;第三个图中有3个小长方形&a…

不同版本的 Redis 的键值对内存占用情况示例

不同版本的 Redis 的键值对内存占用情况示例 文章目录 不同版本的 Redis 的键值对内存占用情况示例Redis 6.0redisObjectdictEntrysds&#x1f340; 数据结构&#x1f340; sdslen() 函数&#x1f340; sdsReqType() 函数&#x1f340; sdsHdrSize() 函数 内存分配 - malloc() …

捕虫游戏-项目制作

前言 同学们前面已经学习了html css javascript基础部分了&#xff0c;为了巩固和熟练前面所学的知识&#xff0c;从今天起&#xff0c;我们要做一个捕虫游戏的项目。通过项目实战夯实基础&#xff0c;将所学的知识真正用到实战中&#xff0c;强化对网页设计的能力&#xff…

用docker快速安装电子白板Excalidraw绘制流程图

注&#xff1a;本文操作以debian12.8 最小化安装环境为host系统。 一、彻底卸载原有的残留 apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras 二、设置docker的安装源 # Add Dockers official G…

【从零开始入门unity游戏开发之——C#篇23】C#面向对象继承——`as`类型转化和`is`类型检查、向上转型和向下转型、里氏替换原则(LSP)

文章目录 一、as类型转化和is类型检查1、as 关键字使用场景&#xff1a;语法&#xff1a;示例&#xff1a;特点&#xff1a; 2、is 关键字使用场景&#xff1a;语法&#xff1a;示例&#xff1a;特点&#xff1a; 3、总结 二、向上转型和向下转型1、向上转型示例&#xff1a; 2…

Android GO 版本锁屏声音无效问题

问题描述 Android go版本 在设置中打开锁屏音开关&#xff0c;息屏灭屏还是无声音 排查 vendor\mediatek\proprietary\packages\apps\SystemUI\src\com\android\systemui\keyguard\KeyguardViewMediator.java private void setupLocked() {...String soundPath Settings.G…

跟着问题学18——transformer模型详解及代码实战(3)Encode编码器

跟着问题学18——transformer模型详解及代码实战&#xff08;1&#xff09; 跟着问题学18——transformer详解(2)多头自注意力机制-CSDN博客 2.3 残差连接 通过自注意力层我们挖掘提取了原始数据的特征&#xff0c;但编码层中会有多个编码器&#xff0c;这会导致网络层数的加…

React系列(八)——React进阶知识点拓展

前言 在之前的学习中&#xff0c;我们已经知道了React组件的定义和使用&#xff0c;路由配置&#xff0c;组件通信等其他方法的React知识点&#xff0c;那么本篇文章将针对React的一些进阶知识点以及React16.8之后的一些新特性进行讲解。希望对各位有所帮助。 一、setState &am…

【原生js案例】移动端如何实现页面的入场和出场动画

好的css动画&#xff0c;能给用户体验带来很大的提升&#xff0c;同时也能增加app的趣味性&#xff0c;给人眼前一亮的感觉。那如何实现这种全屏的弹窗入场和退场的动画 实现效果 代码实现 UI样式美化 #musicDetails{width: 100%;height: 100%;top:0;left:0;position: absol…

Pyqt6在lineEdit中输入文件名称并创建或删除JSON文件

1、创建JSON文件 代码 import osdef addModulekeyWordFile(self):if "" ! self.lineEdit_module.text():moduleFile self.lineEdit_module.text() .jsonelse:self.toolLogPrinting(请输入模块名称)returnfilePath modulekeyWordFileDir moduleFileif os.path.e…

鸿蒙UI开发——组件滤镜效果

1、概 述 ArkUI为组件提供了滤镜效果设置&#xff0c;背景滤镜、前景滤镜、合成滤镜。我们可以通过以下方式为组件设置对应的滤镜效果。 Text(前景滤镜)// ..其他属性.foregroundFilter(filterTest1) // 通过 foregroundFilter 设置模糊效果Text(背景滤镜)// ...其他属性.bac…

均方误差损失函数(MSE)和交叉熵损失函数详解

为什么需要损失函数 前面的文章我们已经从模型角度介绍了损失函数&#xff0c;对于神经网络的训练&#xff0c;首先根据特征输入和初始的参数&#xff0c;前向传播计算出预测结果&#xff0c;然后与真实结果进行比较&#xff0c;得到它们之间的差值。 损失函数又可称为代价函…

抓包 127.0.0.1 (loopback) 使用 tcpdump+wireshark

直接使用 wireshark无法抓取 127.0.0.1环回的数据包&#xff0c;一种解决方法是先传到路由器再返回&#xff0c;但这样可能造成拥塞。 Linux 先使用tcpdump抓包并输出为二进制文件&#xff0c;然后wireshark打开。 比如 sudo tcpdump -i lo src host localhost and dst host…

免费GIS工具箱:轻松将glb文件转换成3DTiles文件

在GIS地理信息系统领域&#xff0c;GLB文件作为GLTF文件的二进制版本&#xff0c;主要用于3D模型数据的存储和展示。然而&#xff0c;GLB文件的使用频率相对较低&#xff0c;这是因为GIS系统主要处理的是地理空间数据&#xff0c;如地图、地形、地貌、植被、水系等&#xff0c;…

安防监控Liveweb视频汇聚融合平台助力执法记录仪高效使用

Liveweb平台可接入的设备除了常见的智能分析网关与摄像头以外 &#xff0c;还可通过GB28181协议接入执法记录仪&#xff0c;实现对执法过程的全程监控与录像&#xff0c;并对执法轨迹与路径进行调阅回看。那么&#xff0c;如何做到执法记录仪高效使用呢&#xff1f; 由于执法记…

【Unity3D】实现可视化链式结构数据(节点数据)

关键词&#xff1a;UnityEditor、可视化节点编辑、Unity编辑器自定义窗口工具 使用Newtonsoft.Json、UnityEditor相关接口实现 主要代码&#xff1a; Handles.DrawBezier(起点&#xff0c;终点&#xff0c;起点切线向量&#xff0c;终点切线向量&#xff0c;颜色&#xff0c;n…

网络安全核心目标CIA

网络安全的核心目标是为关键资产提供机密性(Confidentiality)、可用性(Availablity)、完整性(Integrity)。作为安全基础架构中的主要的安全目标和宗旨&#xff0c;机密性、可用性、完整性频频出现&#xff0c;被简称为CIA&#xff0c;也被成为你AIC&#xff0c;只是顺序不同而已…

[项目代码] YOLOv8 遥感航拍飞机和船舶识别 [目标检测]

项目代码下载链接 &#xff1c;项目代码&#xff1e;YOLO 遥感航拍飞机和船舶识别&#xff1c;目标检测&#xff1e;https://download.csdn.net/download/qq_53332949/90163939YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为…

去雾Cycle-GAN损失函数

文章目录 GAN-LossIdentity-LossDP-lossCycle-Loss G和F都是生成器 G是hazy → \to → gt F是gt → \to → hazy D y D_y Dy​判别无雾图是真实还是生成的&#xff1f; D x D_x Dx​判别有雾图是真实还是生成的&#xff1f; GAN-Loss 在 DAM-CCGAN 中存在两个判别器 D x D_x D…