当前位置: 首页 > news >正文

【堆】最大堆、最小堆以及GO语言的实现

堆是计算机科学中一种特别的完全二叉树结构,在优先队列、图算法和排序算法中有广泛应用。本文将从概念、原理和实现等方面详细介绍堆这一重要的数据结构。

1. 堆的基本概念

1.1 什么是堆?

堆(Heap)是一种特殊的完全二叉树,具有以下特性:

  • 结构性质:堆是一个完全二叉树,即除了最后一层,其他层的节点数都是最大的,且最后一层的节点都集中在左侧。
  • 堆序性质:根据堆的类型(最大堆或最小堆),节点的值满足特定的排序关系。

1.2 堆的类型

堆主要分为两种类型:

  1. 最大堆(Max Heap):任何节点的值都大于或等于其子节点的值。因此,根节点包含堆中的最大值。

  2. 最小堆(Min Heap):任何节点的值都小于或等于其子节点的值。因此,根节点包含堆中的最小值。

1.3 堆的应用

  • 实现优先队列
  • 堆排序算法
  • 图算法中的Dijkstra算法、Prim算法
  • 中位数和第K大元素问题
  • 定时器(Timer)的实现

2. 堆的原理与表示

2.1 数组表示

虽然堆是二叉树结构,但通常使用数组来实现,这样不需要存储指针就能访问节点的父节点和子节点。对于数组中索引为i的元素:

  • 父节点的索引:(i-1)/2(整数除法)
  • 左子节点的索引:2*i + 1
  • 右子节点的索引:2*i + 2

这种表示方法高效且节省空间,也符合完全二叉树的结构特性。

2.2 最大堆的原理

在最大堆中,每个节点的值都大于或等于其子节点的值。这意味着:

  • 根节点是堆中的最大元素
  • 任一节点的值都大于或等于其子树中的所有节点值
  • 从根到叶子的每条路径都是递减的

例如,以下是一个最大堆:

       100/   \19    36/ \    / \17  3  25  1/ \2   7

2.3 最小堆的原理

在最小堆中,每个节点的值都小于或等于其子节点的值。这意味着:

  • 根节点是堆中的最小元素
  • 任一节点的值都小于或等于其子树中的所有节点值
  • 从根到叶子的每条路径都是递增的

例如,以下是一个最小堆:

        1/ \2   5/ \  / \3  4 13 10/ \8  9

3. 堆的基本操作

3.1 插入操作(Insert)

将新元素添加到堆中的过程:

  1. 将新元素添加到堆的末尾(数组的末尾)
  2. 将新元素向上调整(上浮),直到满足堆性质
上浮(Sift Up)过程:
function siftUp(heap, index):parent = (index - 1) / 2// 对于最大堆,如果当前节点大于父节点,则交换// 对于最小堆,如果当前节点小于父节点,则交换if heap[parent] < heap[index]:  // 最大堆的情况swap(heap[parent], heap[index])siftUp(heap, parent)

3.2 删除最大(或最小)元素

堆的设计使得获取并删除最值元素(根节点)变得高效:

  1. 保存根节点的值(作为返回值)
  2. 将堆的最后一个元素移到根节点位置
  3. 将根节点向下调整(下沉),直到满足堆性质
  4. 返回保存的根节点值
下沉(Sift Down)过程:
function siftDown(heap, index):largest = indexleft = 2 * index + 1right = 2 * index + 2// 找到最大子节点(最大堆)或最小子节点(最小堆)if left < heap.size && heap[left] > heap[largest]:  // 最大堆的情况largest = leftif right < heap.size && heap[right] > heap[largest]:  // 最大堆的情况largest = right// 如果需要交换if largest != index:swap(heap[index], heap[largest])siftDown(heap, largest)

3.3 堆化(Heapify)

将一个无序数组转换为堆的过程称为堆化。有两种主要方法:

  1. 自底向上堆化:从最后一个非叶子节点开始,对每个节点执行下沉操作,直到根节点。

    function buildHeap(array):for i = array.size/2 - 1 to 0:siftDown(array, i)
    
  2. 自顶向下堆化:从空堆开始,逐个插入元素并执行上浮操作。

自底向上的堆化方法更高效,时间复杂度为O(n),而不是自顶向下的O(n log n)。

4. 最大堆的实现

下面是一个用Go语言实现的最大堆:

package mainimport ("fmt"
)// MaxHeap 表示最大堆数据结构
type MaxHeap struct {array []int
}// 插入元素
func (h *MaxHeap) Insert(key int) {h.array = append(h.array, key)h.siftUp(len(h.array) - 1)
}// 上浮操作
func (h *MaxHeap) siftUp(index int) {for parent := (index - 1) / 2; index > 0 && h.array[parent] < h.array[index]; index, parent = parent, (parent-1)/2 {h.array[index], h.array[parent] = h.array[parent], h.array[index]}
}// 获取并删除最大元素
func (h *MaxHeap) ExtractMax() (int, error) {if len(h.array) == 0 {return 0, fmt.Errorf("heap is empty")}max := h.array[0]// 将最后一个元素放到根节点lastIndex := len(h.array) - 1h.array[0] = h.array[lastIndex]h.array = h.array[:lastIndex]// 下沉操作if len(h.array) > 0 {h.siftDown(0)}return max, nil
}// 下沉操作
func (h *MaxHeap) siftDown(index int) {maxIndex := indexsize := len(h.array)for {left := 2*index + 1right := 2*index + 2if left < size && h.array[left] > h.array[maxIndex] {maxIndex = left}if right < size && h.array[right] > h.array[maxIndex] {maxIndex = right}if maxIndex == index {return}h.array[index], h.array[maxIndex] = h.array[maxIndex], h.array[index]index = maxIndex}
}// 从数组构建堆
func (h *MaxHeap) BuildHeap(arr []int) {h.array = arrfor i := len(h.array)/2 - 1; i >= 0; i-- {h.siftDown(i)}
}func main() {h := &MaxHeap{}h.BuildHeap([]int{4, 10, 3, 5, 1})fmt.Println("Max Heap:")fmt.Println(h.array)max, _ := h.ExtractMax()fmt.Println("Extracted max:", max)fmt.Println("Heap after extraction:", h.array)h.Insert(15)fmt.Println("Heap after insertion:", h.array)
}

5. 最小堆的实现

最小堆与最大堆的实现几乎相同,只需改变比较逻辑:

package mainimport ("fmt"
)// MinHeap 表示最小堆数据结构
type MinHeap struct {array []int
}// 插入元素
func (h *MinHeap) Insert(key int) {h.array = append(h.array, key)h.siftUp(len(h.array) - 1)
}// 上浮操作
func (h *MinHeap) siftUp(index int) {for parent := (index - 1) / 2; index > 0 && h.array[parent] > h.array[index]; index, parent = parent, (parent-1)/2 {h.array[index], h.array[parent] = h.array[parent], h.array[index]}
}// 获取并删除最小元素
func (h *MinHeap) ExtractMin() (int, error) {if len(h.array) == 0 {return 0, fmt.Errorf("heap is empty")}min := h.array[0]// 将最后一个元素放到根节点lastIndex := len(h.array) - 1h.array[0] = h.array[lastIndex]h.array = h.array[:lastIndex]// 下沉操作if len(h.array) > 0 {h.siftDown(0)}return min, nil
}// 下沉操作
func (h *MinHeap) siftDown(index int) {minIndex := indexsize := len(h.array)for {left := 2*index + 1right := 2*index + 2if left < size && h.array[left] < h.array[minIndex] {minIndex = left}if right < size && h.array[right] < h.array[minIndex] {minIndex = right}if minIndex == index {return}h.array[index], h.array[minIndex] = h.array[minIndex], h.array[index]index = minIndex}
}// 从数组构建堆
func (h *MinHeap) BuildHeap(arr []int) {h.array = arrfor i := len(h.array)/2 - 1; i >= 0; i-- {h.siftDown(i)}
}func main() {h := &MinHeap{}h.BuildHeap([]int{4, 10, 3, 5, 1})fmt.Println("Min Heap:")fmt.Println(h.array)min, _ := h.ExtractMin()fmt.Println("Extracted min:", min)fmt.Println("Heap after extraction:", h.array)h.Insert(0)fmt.Println("Heap after insertion:", h.array)
}

6. 堆的性能分析

6.1 时间复杂度

操作时间复杂度
插入元素O(log n)
删除最大/最小元素O(log n)
获取最大/最小元素O(1)
构建堆O(n)

6.2 空间复杂度

堆的空间复杂度为O(n),其中n是堆中元素的数量。

相关文章:

  • 【Java Lambda表达式详解】
  • bellard.org‌ : QuickJS 如何使用 qjs 执行 js 脚本
  • 一种实波束前视扫描雷达目标二维定位方法——论文阅读
  • 搭建一个 gRPC 服务端和客户端
  • 青少年ctf练习平台--做题wp(2)
  • 使用python加edge-tts实现文字转语音
  • C++ 简单工厂模式详解
  • 游戏开发的TypeScript(3)匿名函数的常见和不常见用法
  • 基于 vue-flow 实现可视化流程图
  • Java学习手册:关系型数据库基础
  • C++ 中 virtual 的作用
  • 什么是函数重载?
  • 【开源免费】二维码批量识别-未来之窗——C#-仙盟创梦IDE
  • 在 Ubuntu 上安装 cPanel
  • 20:深度学习-多层感知器原理
  • Linxu基本操作
  • 计算机系统结构 第二章 :缓存优化
  • Java中深拷贝与浅拷贝的深入探讨
  • C++抽象基类三重防线:纯虚函数与保护构造的深度实践
  • springAop代理责任链模式源码解析
  • 新华社评论员:在推进中国式现代化的宽广舞台上绽放青春光彩
  • 国际观察|韩国在政局多重不确定性中迎接总统选举
  • 乌副总理:乌美签署矿产协议
  • 新华时评:防范安全事故须臾不可放松
  • 城市更新·简报│中央财政支持城市更新,倾斜超大特大城市
  • 国新办发布《关于新冠疫情防控与病毒溯源的中方行动和立场》白皮书