数据结构与算法 -> 大顶堆与小顶堆
一、大顶堆
- 大顶堆是一种数据结构,它是一颗完全二叉树,并且满足以下性质:
- 每个节点的值都大于或等于它的子节点的值
- 因此,大顶堆的根节点(也称为堆顶)总是最大的元素
二、小顶堆
- 小顶堆也是一种数据结构,它是一颗完全二叉树,并且满足以下性质:
- 每个节点的值都小于或等于它的子节点的值
- 因此,小顶堆的根节点(也称为堆顶)总是最小的元素
三、主要区别
小顶堆和大顶堆是堆这种数据结构的两种形式,它们都是一颗完全二叉树,并且满足特定的性质。小顶堆的堆顶元素是最小的元素,而大顶堆的堆顶元素是最大的元素。小顶堆和大顶堆常用于实现优先队列,其操作的时间复杂度通常为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;
}
}
}
数据结构与算法 -> 大顶堆与小顶堆的更多相关文章
- 堆排序(大顶堆、小顶堆)----C语言
堆排序 之前的随笔写了栈(顺序栈.链式栈).队列(循环队列.链式队列).链表.二叉树,这次随笔来写堆 1.什么是堆? 堆是一种非线性结构,(本篇随笔主要分析堆的数组实现)可以把堆看作一个数组,也可以被 ...
- HDU 3791 二叉搜索树 (数据结构与算法实验题 10.2 小明) BST
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3791 中文题不说题意. 建立完二叉搜索树后进行前序遍历或者后序遍历判断是否一样就可以了. 跟这次的作业第 ...
- 《排序算法》——堆排序(大顶堆,小顶堆,Java)
十大算法之堆排序: 堆的定义例如以下: n个元素的序列{k0,k1,...,ki,-,k(n-1)}当且仅当满足下关系时,称之为堆. " ki<=k2i,ki<=k2i+1;或k ...
- 数据结构:堆排序 (python版) 小顶堆实现从大到小排序 | 大顶堆实现从小到大排序
#!/usr/bin/env python # -*- coding:utf-8 -*- ''' Author: Minion-Xu 小堆序实现从大到小排序,大堆序实现从小到大排序 重点的地方:小堆序 ...
- heap c++ 操作 大顶堆、小顶堆
在C++中,虽然堆不像 vector, set 之类的有已经实现的数据结构,但是在 algorithm.h 中实现了一些相关的模板函数.下面是一些示例应用 http://www.cplusplus.c ...
- 大顶堆与小顶堆应用---寻找前k小数
vector<int> getLeastNumber(vector<int>& arr,int k){ vector<int> vec(k,); if(== ...
- 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 ...
- wikioi 2573 大顶堆与小顶堆并用
题目描写叙述 Description 我们使用黑匣子的一个简单模型.它能存放一个整数序列和一个特别的变量i.在初始时刻.黑匣子为空且i等于0. 这个黑匣子能运行一系列的命令.有两类命令: ADD(x) ...
- 数据结构与算法(Java版)_堆
完全二叉树叫做堆. 完全二叉树就是最后一个节点之前不允许有不满的节点,就是不允许有空洞. 可以使用数组来做完全二叉树(堆). 堆分为大顶堆和小顶堆.大顶堆就是根节点上的数字是最大的,小顶堆就是根节点上 ...
- Java数据结构和算法(五)二叉排序树(BST)
Java数据结构和算法(五)二叉排序树(BST) 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 二叉排序树(Binary S ...
随机推荐
- 研发效能|DevOps 已死平台工程永存带来的焦虑
最近某位大神在推特上发了一个帖子,结果引来了国内众多卖课机构.培训机构的狂欢,开始贩卖焦虑,其实「平台工程」也不是什么特别高深莫测的东西.闲得无聊,把这位大神的几个帖子薅了下来,你看过之后就会觉得没啥 ...
- scrapy出现SSL问题 如何解决? <twisted.python.failure.Failure OpenSSL.SSL.Error: [('SSL routines', '', 'unsafe legacy renegotiation disabled')]>
问题:<twisted.python.failure.Failure OpenSSL.SSL.Error: [('SSL routines', '', 'unsafe legacy renego ...
- VideoPipe可视化视频结构化框架新增功能详解(2022-11-4)
VideoPipe从国庆节上线源代码到现在经历过了一个月时间,期间吸引了若干小伙伴的参与,现将本阶段新增内容总结如下,有兴趣的朋友可以加微信拉群交流. 项目地址:https://github.com/ ...
- 基于docker和cri-dockerd部署kubernetes v1.25.3
基于docker和cri-dockerd部署kubernetes v1.25.3 1.环境准备 1-1.主机清单 主机名 IP地址 系统版本 k8s-master01 k8s-master01.wan ...
- Java读取txt文件、excel文件的方法
Java读取txt文件.excel文件的方法 1.读取txt文件 public static String getFileContent(String filePath,String charset) ...
- JUC学习笔记——共享模型之管程
JUC学习笔记--共享模型之管程 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的管程部分 我们会分为以下几部分进行介绍: 共享问题 共享问题解决方案 线程安全分析 Monitor ...
- ValidList
package com.dlzb.enterprising.config; import javax.validation.Valid; import java.util.*; public clas ...
- tools2
[对身份证的校验] //身份证的校验 import java.util.stream.IntStream; /** * 身份证号码验证 * 1.号码的结构 * 公民身份号码是特征组合码,由十七位数字本 ...
- CSP2022-J/S 游记
Day -2147483648 初赛 J组: 水. 单选没啥好说的,那道联通的傻掉挂了 \(2\). 读程 \(T1\) 手搓,\(T2\) 找规律(判断第一题蒙的,懒得算),\(T3\) 没注意 \ ...
- MLP(SGD or Adam) Perceptron Neural Network Working by Pytorch(including data preprocessing)
通过MLP多层感知机神经网络训练模型,使之能够根据sonar的六十个特征成功预测物体是金属还是石头.由于是简单的linearr线性仿射层,所以网络模型的匹配度并不高. 这是我的第一篇随笔,就拿这个来练 ...