最大堆和最小堆 实现思路

目录

一、是什么

二、实现方式

三、总结


一、是什么

1.最大堆 (大顶堆/大根堆): 完全二叉树,每个节点元素都大于他的直接左节点和右节点,根节点是最大值

2.最小堆 (小顶堆/小根堆):完全二叉树,每个节点元素都小于他的直接左节点和右节点,根节点是最小值

二、实现方式

1.数据结构 :一维数组

当前索引 index

父节点索引 Math.floor( index-1)/2

左节点index*2+1

右节点 index*2+2

2.需要实现的方法

  1. add 添加元素push+ heapifyUp(上浮到合适的位置,满足最大堆/最小堆定义)

2)heapifyUp方法(把添加的元素上浮到合适的位置):从数组末尾开始找父元素,如果当前元素和父元素的大小关系不满足定义就交换,满足就停止循环。

最小堆:当前的元素比父元素还小交换,当前元素索引变成父元素索引;直到当前元素大于父元素停止循环

最大堆:当前的元素比父元素还大交换,当前元素索引变成父元素的索引;直到当前元素小于父元素停止循环。

3)remove取出堆顶元素copy last +pop+heapifyDown暂存数组第一个元素最后返回;用最后一个元素覆盖第一个元素当作根节点;删除最后一个元素;heapifyDown把新的根节点(值为最后一个元素)下沉到合适的位置

4)heapifyDown下沉:把堆顶的元素和 左右子节点比较放到合适的位置使得 最大堆/最小堆 符合定义。

最小堆:如果左右子节点的最小值比当前元素小则交换,当前元素索引变成左右子节点的最小值对应的索引;直到当前元素小于左右子节点的最小值停止循环

最大堆:如果左右子节点的最大值比 当前元素大交换,当前元素索引变成左右子节点最大值对应的索引;直到当前元素大于 左右子节点的最大值停止循环

3.代码实现

最小堆

javascript 复制代码
/**
 * 最小堆
 * 定义: 完全二叉树,每个节点的值都小于或等于其子节点的值
 * 实现方式: 数组
 * 应用场景: 优先队列、TopK问题、中位数问题等
 * 优点: 快速获取最小值,快速插入,快速删除
 * 缺点: 不能快速获取最大值
 */
class MinHeap {
  constructor() {
    this.heap = []
  }
  // 添加元素
  add(value) {
    this.heap.push(value)
    this.heapifyUp()
  }
  // 上浮操作
  heapifyUp() {
    let index = this.heap.length - 1
    while (index > 0) {
      let parentIndex = Math.floor((index - 1) / 2)
      // 如果当前元素小于父元素,则交换位置
      if (this.heap[index] < this.heap[parentIndex]) {
        ;[this.heap[index], this.heap[parentIndex]] = [this.heap[parentIndex], this.heap[index]]
        index = parentIndex
      } else {
        // 如果当前元素已经大于等于父元素,则停止上浮
        break
      }
    }
  }
  // 下沉操作
  heapifyDown() {
    let index = 0
    while (index < this.heap.length) {
      let leftChildIndex = index * 2 + 1
      let rightChildIndex = index * 2 + 2
      // 找出当前元素的左右子元素中较小的那个(最小的要放到顶部)
      let minIndex = index
      if (leftChildIndex < this.heap.length) {
        if (this.heap[leftChildIndex] < this.heap[index]) {
          minIndex = leftChildIndex
        }
      }
      if (rightChildIndex < this.heap.length) {
        if (this.heap[rightChildIndex] < this.heap[minIndex]) {
          minIndex = rightChildIndex
        }
      }
      if (minIndex !== index) {
        ;[this.heap[index], this.heap[minIndex]] = [this.heap[minIndex], this.heap[index]]
        index = minIndex
      } else {
        // 如果当前元素已经大于等于左右子元素,则停止下沉
        break
      }
    }
  }

  //取出最小值
  remove() {
    let value = this.heap[0]
    // 将最后一个元素放到顶部
    this.heap[0] = this.heap[this.heap.length - 1]
    this.heap.pop()
    if (!this.isEmpty()) {
      this.heapifyDown()
    }
    return value
  }
  getMin() {
    if (this.isEmpty()) {
      return null
    }
    return this.heap[0]
  }
  size() {
    return this.heap.length
  }
  isEmpty() {
    return this.heap.length === 0
  }
}

const minHeap = new MinHeap()
minHeap.add(3)
minHeap.add(2)
minHeap.add(1)
minHeap.add(4)
minHeap.add(5)
console.log('minHeap start=============', minHeap.heap)
console.log(minHeap.getMin()) // 1
minHeap.remove()
console.log(minHeap.getMin()) // 2
minHeap.remove()
console.log(minHeap.getMin()) // 3
minHeap.remove()
console.log(minHeap.getMin()) // 4
minHeap.remove()
console.log(minHeap.getMin()) // 5
minHeap.remove()
console.log(minHeap.getMin()) // null
console.log('minHeap end=============')

最大堆

javascript 复制代码
/**
 * 最大堆
 * 定义: 完全二叉树,每个节点的值都大于或等于其子节点的值
 * 实现方式: 数组
 * 应用场景: 优先队列、TopK问题、中位数问题等
 * 优点: 快速获取最大值,快速插入,快速删除
 * 缺点: 不能快速获取最小值
 */
class MaxHeap {
  heap = []
  constructor() {
    this.heap = []
  }
  add(value) {
    this.heap.push(value)
    this.heapifyUp()
  }

  // 上浮操作
  heapifyUp() {
    let index = this.heap.length - 1
    while (index > 0) {
      let parentIndex = Math.floor((index - 1) / 2)
      // 如果当前元素大于父元素,则交换位置
      if (this.heap[index] > this.heap[parentIndex]) {
        ;[this.heap[index], this.heap[parentIndex]] = [this.heap[parentIndex], this.heap[index]]
        index = parentIndex
      } else {
        // 如果当前元素已经小于等于父元素,则停止上浮
        break
      }
    }
  }

  //取出最大值
  remove() {
    let value = this.heap[0]
    this.heap[0] = this.heap[this.heap.length - 1]
    this.heap.pop()
    if (!this.isEmpty()) {
      this.heapifyDown()
    }
    return value
  }

  // 下沉操作
  heapifyDown() {
    let index = 0
    while (index < this.heap.length) {
      let leftChildIndex = index * 2 + 1
      let rightChildIndex = index * 2 + 2
      // 找出当前元素的左右子元素中较大的那个(最大的要放到顶部)
      let maxIndex = index
      if (leftChildIndex < this.heap.length) {
        if (this.heap[leftChildIndex] > this.heap[maxIndex]) {
          maxIndex = leftChildIndex
        }
      }
      if (rightChildIndex < this.heap.length) {
        if (this.heap[rightChildIndex] > this.heap[maxIndex]) {
          maxIndex = rightChildIndex
        }
      }
      if (maxIndex !== index) {
        ;[this.heap[index], this.heap[maxIndex]] = [this.heap[maxIndex], this.heap[index]]
        index = maxIndex
      } else {
        // 如果当前元素已经大于左右子元素,则停止下沉
        break
      }
    }
  }
  getMax() {
    if (this.isEmpty()) {
      return null
    }
    return this.heap[0]
  }
  size() {
    return this.heap.length
  }
  isEmpty() {
    return this.heap.length === 0
  }
}
const maxHeap = new MaxHeap()
maxHeap.add(3)
maxHeap.add(2)
maxHeap.add(1)
maxHeap.add(4)
maxHeap.add(5)
console.log('maxHeap start=============', maxHeap.heap)
console.log(maxHeap.getMax()) // 5
maxHeap.remove()
console.log(maxHeap.getMax()) // 4
maxHeap.remove()
console.log(maxHeap.getMax()) // 3
maxHeap.remove()
console.log(maxHeap.getMax()) // 2
maxHeap.remove()
console.log(maxHeap.getMax()) // 1
maxHeap.remove()
console.log(maxHeap.getMax()) // null
console.log('maxHeap end=============')

三、总结

1.最大堆和最小堆都是基于一维数组实现的数据结构。添加和取出堆顶数据都需要保证父节点和直接左右子节点的大小关系,所以需要 heapifyUp和heapifyDown操作来维持堆的的结构

2.heapifyUp添加 时,添加的元素和父元素比较交换直到满足定义

3.heapifyDown :取出堆顶元素 时,新的根节点(最后一个元素)和左右子节点比较交换直到满足定义

4.应用场景取最值;前TopK(依次取k次堆顶元素);

/*

希望对你有帮助!

如有错误,欢迎指正,谢谢!

*/

相关推荐
__WanG5 小时前
JavaTuples 库分析
java
renhongxia15 小时前
如何基于知识图谱进行故障原因、事故原因推理,需要用到哪些算法
人工智能·深度学习·算法·机器学习·自然语言处理·transformer·知识图谱
坚持就完事了5 小时前
数据结构之树(Java实现)
java·算法
算法备案代理5 小时前
大模型备案与算法备案,企业该如何选择?
人工智能·算法·大模型·算法备案
Monly215 小时前
Java:修改打包配置文件
java·开发语言
roman_日积跬步-终至千里5 小时前
【架构设计与实现】动态数据源切换:核心代码实现手册
java
XiaoFan0126 小时前
免密批量抓取日志并集中输出
java·linux·服务器
顾北126 小时前
MCP服务端开发:图片搜索助力旅游计划
java·spring boot·dubbo
我命由我123456 小时前
Android 广播 - 静态注册与动态注册对广播接收器实例创建的影响
android·java·开发语言·java-ee·android studio·android-studio·android runtime