一、大顶堆

  • 大顶堆是一种数据结构,它是一颗完全二叉树,并且满足以下性质:

    • 每个节点的值都大于或等于它的子节点的值
    • 因此,大顶堆的根节点(也称为堆顶)总是最大的元素

二、小顶堆

  • 小顶堆也是一种数据结构,它是一颗完全二叉树,并且满足以下性质:

    • 每个节点的值都小于或等于它的子节点的值
    • 因此,小顶堆的根节点(也称为堆顶)总是最小的元素

三、主要区别

  • 小顶堆和大顶堆是堆这种数据结构的两种形式,它们都是一颗完全二叉树,并且满足特定的性质。小顶堆的堆顶元素是最小的元素,而大顶堆的堆顶元素是最大的元素。小顶堆和大顶堆常用于实现优先队列,其操作的时间复杂度通常为O(log n)。

  • 小顶堆和大顶堆的区别在于它们的堆顶元素分别是最小的和最大的元素。因此,小顶堆通常用于求出数据集中的最小值,而大顶堆通常用于求出数据集中的最大值

四、算法模板

1.大顶堆模板

// 大顶堆模板
class MaxHeap {
constructor() {
this.heap = [];
} // 返回堆的大小
size() {
return this.heap.length;
} // 向堆中插入一个新元素
insert(val) {
// 将新元素添加到堆的末尾
this.heap.push(val);
// 调整堆使其满足大顶堆的性质
this.heapifyUp();
} // 删除堆顶元素
deleteTop() {
// 如果堆为空,则直接返回
if (this.size() === 0) return;
// 将堆顶元素与堆的最后一个元素交换
let temp = this.heap[0];
this.heap[0] = this.heap[this.size() - 1];
this.heap[this.size() - 1] = temp;
// 将堆的最后一个元素从堆中删除
this.heap.pop();
// 调整堆使其满足大顶堆的性质
this.heapifyDown();
} // 调整堆使其满足大顶堆的性质
heapifyUp() {
// 获取新插入的元素的索引
let index = this.size() - 1;
// 循环,直到该元素的值大于等于它的父节点的值
while (index > 0 && this.heap[index] > this.heap[this.parent(index)]) {
// 将该元素与它的父节点交换
let temp = this.heap[index];
this.heap[index] = this.heap[this.parent(index)];
this.heap[this.parent(index)] = temp;
// 更新索引
index = this.parent(index);
}
} // 调整堆使其满足大顶堆的性质
heapifyDown() {
// 获取堆顶元素的索引
let index = 0;
// 循环,直到该元素的值小于等于它的子节点的值
while (index < this.size() && this.heap[index] < this.maxChildValue(index)) {
// 获取该元素的子节点中的最大值的索引
let maxChildIndex = this.maxChildIndex(index);
// 将该元素与它的子节点中的最大值交换
let temp = this.heap[index];
this.heap[index] = this.heap[maxChildIndex];
this.heap[maxChildIndex] = temp;
// 更新索引
index = maxChildIndex;
}
} // 返回给定索引的元素的父节点的索引
parent(index) {
return Math.floor((index - 1) / 2);
} // 返回给定索引的元素的左子节点的索引
leftChild(index) {
return index * 2 + 1;
} // 返回给定索引的元素的右子节点的索引
rightChild(index) {
return index * 2 + 2;
} // 返回给定索引的元素的子节点中的最大值
maxChildValue(index) {
let leftChildIndex = this.leftChild(index);
let rightChildIndex = this.rightChild(index);
if (leftChildIndex >= this.size()) return -Infinity;
if (rightChildIndex >= this.size()) return this.heap[leftChildIndex];
return Math.max(this.heap[leftChildIndex], this.heap[rightChildIndex]);
} // 返回给定索引的元素的子节点中的最大值的索引
maxChildIndex(index) {
let leftChildIndex = this.leftChild(index);
let rightChildIndex = this.rightChild(index);
if (leftChildIndex >= this.size()) return -1;
if (rightChildIndex >= this.size()) return leftChildIndex;
return this.heap[leftChildIndex] > this.heap[rightChildIndex] ? leftChildIndex : rightChildIndex;
}
}

2.小顶堆模板

// 小顶堆模板
class MinHeap {
constructor() {
// 初始化堆数组
this.heap = [];
} // 获取父节点的索引
getParentIndex(childIndex) {
return Math.floor((childIndex - 1) / 2);
} // 获取左子节点的索引
getLeftChildIndex(parentIndex) {
return 2 * parentIndex + 1;
} // 获取右子节点的索引
getRightChildIndex(parentIndex) {
return 2 * parentIndex + 2;
} // 判断是否存在父节点
hasParent(index) {
return this.getParentIndex(index) >= 0;
} // 判断是否存在左子节点
hasLeftChild(index) {
return this.getLeftChildIndex(index) < this.heap.length;
} // 判断是否存在右子节点
hasRightChild(index) {
return this.getRightChildIndex(index) < this.heap.length;
} // 获取左子节点的值
leftChild(index) {
return this.heap[this.getLeftChildIndex(index)];
} // 获取右子节点的值
rightChild(index) {
return this.heap[this.getRightChildIndex(index)];
} // 获取父节点的值
parent(index) {
return this.heap[this.getParentIndex(index)];
} // 交换堆中两个节点的值
swap(index1, index2) {
[this.heap[index1], this.heap[index2]] = [this.heap[index2], this.heap[index1]];
} // 获取堆顶元素
peek() {
if (this.heap.length === 0) {
throw new Error('Heap is empty');
}
return this.heap[0];
} // 取出堆顶元素,并重新排序
poll() {
if (this.heap.length === 0) {
throw new Error('Heap is empty');
}
const item = this.heap[0];
this.heap[0] = this.heap.pop();
this.heapifyDown();
return item;
} // 向堆中插入新元素,并重新排序
add(item) {
this.heap.push(item);
this.heapifyUp();
} // 从下往上重新排序
heapifyUp() {
let index = this.heap.length - 1;
// 只要当前节点有父节点,并且父节点的值比当前节点的值大,就交换它们的值
while (this.hasParent(index) && this.parent(index) > this.heap[index]) {
this.swap(this.getParentIndex(index), index);
index = this.getParentIndex(index);
}
} // 从上往下重新排序
heapifyDown() {
let index = 0;
// 只要当前节点有左子节点
while (this.hasLeftChild(index)) {
let smallerChildIndex = this.getLeftChildIndex(index);
// 如果当前节点有右子节点,并且右子节点的值比左子节点的值小,就把右子节点的索引赋给smallerChildIndex
if (this.hasRightChild(index) && this.rightChild(index) < this.leftChild(index)) {
smallerChildIndex = this.getRightChildIndex(index);
}
// 如果当前节点的值已经比子节点的值小,就退出循环
if (this.heap[index] < this.heap[smallerChildIndex]) {
break;
} else {
// 否则交换它们的值,并继续循环
this.swap(index, smallerChildIndex);
}
index = smallerChildIndex;
}
}
}

五、力扣实操

1.大顶堆例题:第 327 场周赛T2 - 执行 K 次操作后的最大分数

  • 代码如下:

    • /**
      * @param {number[]} nums
      * @param {number} k
      * @return {number}
      */
      var maxKelements = function(nums, k) {
      let score = []
      let heap = new MaxHeap()
      for (let item of nums) {
      heap.insert(item)
      }
      while (k) {
      let max = heap.heap[0]
      score.push(max)
      max = Math.ceil(max / 3)
      heap.heap[0] = max
      heap.heapifyDown()
      k--
      }
      return score.reduce((item, total) => { return item + total }, 0)
      }; // 大顶堆模板
      class MaxHeap {
      constructor() {
      this.heap = [];
      } // 返回堆的大小
      size() {
      return this.heap.length;
      } // 向堆中插入一个新元素
      insert(val) {
      // 将新元素添加到堆的末尾
      this.heap.push(val);
      // 调整堆使其满足大顶堆的性质
      this.heapifyUp();
      } // 删除堆顶元素
      deleteTop() {
      // 如果堆为空,则直接返回
      if (this.size() === 0) return;
      // 将堆顶元素与堆的最后一个元素交换
      let temp = this.heap[0];
      this.heap[0] = this.heap[this.size() - 1];
      this.heap[this.size() - 1] = temp;
      // 将堆的最后一个元素从堆中删除
      this.heap.pop();
      // 调整堆使其满足大顶堆的性质
      this.heapifyDown();
      } // 调整堆使其满足大顶堆的性质
      heapifyUp() {
      // 获取新插入的元素的索引
      let index = this.size() - 1;
      // 循环,直到该元素的值大于等于它的父节点的值
      while (index > 0 && this.heap[index] > this.heap[this.parent(index)]) {
      // 将该元素与它的父节点交换
      let temp = this.heap[index];
      this.heap[index] = this.heap[this.parent(index)];
      this.heap[this.parent(index)] = temp;
      // 更新索引
      index = this.parent(index);
      }
      } // 调整堆使其满足大顶堆的性质
      heapifyDown() {
      // 获取堆顶元素的索引
      let index = 0;
      // 循环,直到该元素的值小于等于它的子节点的值
      while (index < this.size() && this.heap[index] < this.maxChildValue(index)) {
      // 获取该元素的子节点中的最大值的索引
      let maxChildIndex = this.maxChildIndex(index);
      // 将该元素与它的子节点中的最大值交换
      let temp = this.heap[index];
      this.heap[index] = this.heap[maxChildIndex];
      this.heap[maxChildIndex] = temp;
      // 更新索引
      index = maxChildIndex;
      }
      } // 返回给定索引的元素的父节点的索引
      parent(index) {
      return Math.floor((index - 1) / 2);
      } // 返回给定索引的元素的左子节点的索引
      leftChild(index) {
      return index * 2 + 1;
      } // 返回给定索引的元素的右子节点的索引
      rightChild(index) {
      return index * 2 + 2;
      } // 返回给定索引的元素的子节点中的最大值
      maxChildValue(index) {
      let leftChildIndex = this.leftChild(index);
      let rightChildIndex = this.rightChild(index);
      if (leftChildIndex >= this.size()) return -Infinity;
      if (rightChildIndex >= this.size()) return this.heap[leftChildIndex];
      return Math.max(this.heap[leftChildIndex], this.heap[rightChildIndex]);
      } // 返回给定索引的元素的子节点中的最大值的索引
      maxChildIndex(index) {
      let leftChildIndex = this.leftChild(index);
      let rightChildIndex = this.rightChild(index);
      if (leftChildIndex >= this.size()) return -1;
      if (rightChildIndex >= this.size()) return leftChildIndex;
      return this.heap[leftChildIndex] > this.heap[rightChildIndex] ? leftChildIndex : rightChildIndex;
      }
      }

2.小顶堆例题:面试题 17.14. 最小K个数 - 力扣(LeetCode)

  • 代码如下:

    • /**
      * @param {number[]} arr
      * @param {number} k
      * @return {number[]}
      */
      var smallestK = function(arr, k) {
      let res = []
      let heap = new MinHeap()
      for (let item of arr) {
      heap.add(item)
      }
      while (k) {
      let min = heap.heap[0]
      res.push(min)
      heap.heap[0] = Number.MAX_VALUE
      min = heap.heap[0]
      heap.heapifyDown()
      k--
      }
      return res
      }; // 小顶堆模板
      class MinHeap {
      constructor() {
      // 初始化堆数组
      this.heap = [];
      } // 获取父节点的索引
      getParentIndex(childIndex) {
      return Math.floor((childIndex - 1) / 2);
      } // 获取左子节点的索引
      getLeftChildIndex(parentIndex) {
      return 2 * parentIndex + 1;
      } // 获取右子节点的索引
      getRightChildIndex(parentIndex) {
      return 2 * parentIndex + 2;
      } // 判断是否存在父节点
      hasParent(index) {
      return this.getParentIndex(index) >= 0;
      } // 判断是否存在左子节点
      hasLeftChild(index) {
      return this.getLeftChildIndex(index) < this.heap.length;
      } // 判断是否存在右子节点
      hasRightChild(index) {
      return this.getRightChildIndex(index) < this.heap.length;
      } // 获取左子节点的值
      leftChild(index) {
      return this.heap[this.getLeftChildIndex(index)];
      } // 获取右子节点的值
      rightChild(index) {
      return this.heap[this.getRightChildIndex(index)];
      } // 获取父节点的值
      parent(index) {
      return this.heap[this.getParentIndex(index)];
      } // 交换堆中两个节点的值
      swap(index1, index2) {
      [this.heap[index1], this.heap[index2]] = [this.heap[index2], this.heap[index1]];
      } // 获取堆顶元素
      peek() {
      if (this.heap.length === 0) {
      throw new Error('Heap is empty');
      }
      return this.heap[0];
      } // 取出堆顶元素,并重新排序
      poll() {
      if (this.heap.length === 0) {
      throw new Error('Heap is empty');
      }
      const item = this.heap[0];
      this.heap[0] = this.heap.pop();
      this.heapifyDown();
      return item;
      } // 向堆中插入新元素,并重新排序
      add(item) {
      this.heap.push(item);
      this.heapifyUp();
      } // 从下往上重新排序
      heapifyUp() {
      let index = this.heap.length - 1;
      // 只要当前节点有父节点,并且父节点的值比当前节点的值大,就交换它们的值
      while (this.hasParent(index) && this.parent(index) > this.heap[index]) {
      this.swap(this.getParentIndex(index), index);
      index = this.getParentIndex(index);
      }
      } // 从上往下重新排序
      heapifyDown() {
      let index = 0;
      // 只要当前节点有左子节点
      while (this.hasLeftChild(index)) {
      let smallerChildIndex = this.getLeftChildIndex(index);
      // 如果当前节点有右子节点,并且右子节点的值比左子节点的值小,就把右子节点的索引赋给smallerChildIndex
      if (this.hasRightChild(index) && this.rightChild(index) < this.leftChild(index)) {
      smallerChildIndex = this.getRightChildIndex(index);
      }
      // 如果当前节点的值已经比子节点的值小,就退出循环
      if (this.heap[index] < this.heap[smallerChildIndex]) {
      break;
      } else {
      // 否则交换它们的值,并继续循环
      this.swap(index, smallerChildIndex);
      }
      index = smallerChildIndex;
      }
      }
      }

数据结构与算法 -> 大顶堆与小顶堆的更多相关文章

  1. 堆排序(大顶堆、小顶堆)----C语言

    堆排序 之前的随笔写了栈(顺序栈.链式栈).队列(循环队列.链式队列).链表.二叉树,这次随笔来写堆 1.什么是堆? 堆是一种非线性结构,(本篇随笔主要分析堆的数组实现)可以把堆看作一个数组,也可以被 ...

  2. HDU 3791 二叉搜索树 (数据结构与算法实验题 10.2 小明) BST

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3791 中文题不说题意. 建立完二叉搜索树后进行前序遍历或者后序遍历判断是否一样就可以了. 跟这次的作业第 ...

  3. 《排序算法》——堆排序(大顶堆,小顶堆,Java)

    十大算法之堆排序: 堆的定义例如以下: n个元素的序列{k0,k1,...,ki,-,k(n-1)}当且仅当满足下关系时,称之为堆. " ki<=k2i,ki<=k2i+1;或k ...

  4. 数据结构:堆排序 (python版) 小顶堆实现从大到小排序 | 大顶堆实现从小到大排序

    #!/usr/bin/env python # -*- coding:utf-8 -*- ''' Author: Minion-Xu 小堆序实现从大到小排序,大堆序实现从小到大排序 重点的地方:小堆序 ...

  5. heap c++ 操作 大顶堆、小顶堆

    在C++中,虽然堆不像 vector, set 之类的有已经实现的数据结构,但是在 algorithm.h 中实现了一些相关的模板函数.下面是一些示例应用 http://www.cplusplus.c ...

  6. 大顶堆与小顶堆应用---寻找前k小数

    vector<int> getLeastNumber(vector<int>& arr,int k){ vector<int> vec(k,); if(== ...

  7. 378. Kth Smallest Element in a Sorted Matrix(大顶堆、小顶堆)

    Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth ...

  8. wikioi 2573 大顶堆与小顶堆并用

    题目描写叙述 Description 我们使用黑匣子的一个简单模型.它能存放一个整数序列和一个特别的变量i.在初始时刻.黑匣子为空且i等于0. 这个黑匣子能运行一系列的命令.有两类命令: ADD(x) ...

  9. 数据结构与算法(Java版)_堆

    完全二叉树叫做堆. 完全二叉树就是最后一个节点之前不允许有不满的节点,就是不允许有空洞. 可以使用数组来做完全二叉树(堆). 堆分为大顶堆和小顶堆.大顶堆就是根节点上的数字是最大的,小顶堆就是根节点上 ...

  10. Java数据结构和算法(五)二叉排序树(BST)

    Java数据结构和算法(五)二叉排序树(BST) 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 二叉排序树(Binary S ...

随机推荐

  1. laravel 报错 AUTH` failed: ERR Client sent AUTH, but no password is set

    明明没有设置redis密码.访问时候却报错 在代码里面的databases.php 改成这样就可以了.predis新版也会有取不到passwor的时候.改成我截图那样也可以.他默认取的是default ...

  2. Vue中引入echarts。

    1.安装 在终端vue项目的文件夹下运行npm install echarts --save安装依赖 可以使用npm install echarts@("这里可以写版本号") -- ...

  3. 算法设计(动态规划应用实验报告)实现基于贪婪技术思想的Prim算法、Dijkstra算法

    一.名称 动态规划法应用 二.目的 1.贪婪技术的基本思想: 2.学会运用贪婪技术解决实际设计应用中碰到的问题. 三.要求 1.实现基于贪婪技术思想的Prim算法: 2.实现基于贪婪技术思想的Dijk ...

  4. 分享个好东西 - 两行前端代码搞定bilibili链接转视频

    只需要在您的要解析B站视频的页面的</body>前面加上下面两行代码即可,脚本会在客户端浏览器里解析container所匹配到的容器里的B站超链接 (如果不是外围有a标签的超链接只是纯粹的 ...

  5. NLP之基于BERT的预测掩码标记和句间关系判断

    BERT @ 目录 BERT 程序步骤 程序步骤 设置基本变量值,数据预处理 构建输入样本 在样本集中随机选取a和b两个句子 把ab两个句子合并为1个模型输入句,在句首加入分类符CLS,在ab中间和句 ...

  6. 9.channels layers

    settings.py配置 # 存储在内存里 CHANNEL_LAYERS = { "default": { "BACKEND": "channels ...

  7. $_SERVER['HTTP_USER_AGENT']:在PHP中HTTP_USER_AGENT是用来获取用户的相关信息的,包括用户使用的浏览器,操作系统等信息

    在PHP中HTTP_USER_AGENT是用来获取用户的相关信息的,包括用户使用的浏览器,操作系统等信息. 我机器:操作系统:WIN7旗舰版 64操作系统 以下为各个浏览器下$_SERVER['HTT ...

  8. kubernetes VS OpenShift浅析

    Kubernetes vs OpenShift浅析 古语有云:"知彼知己,百战不殆.不知彼而知己,一胜一负.不知彼,不知己,每战必殆." 这句话同样也适用于技术体系.无论我们在落地 ...

  9. Debian安装WPS的方法

    1.防止安装失败,请尽量重启电脑,关闭系统的软件商店,因为商店的权限可能会锁住pkg的配置文件,导致无法安装wps. 2.将原机残废的WPS卸载干净,卸载方法:手动或命令行操作. sudo apt r ...

  10. OpenHarmony移植案例: build lite源码分析之hb命令__entry__.py

    摘要:本文介绍了build lite 轻量级编译构建系统hb命令的源码,主要分析了_\entry__.py文件. 本文分享自华为云社区<移植案例与原理 - build lite源码分析 之 hb ...