一、大顶堆

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

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

二、小顶堆

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

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

三、主要区别

  • 小顶堆和大顶堆是堆这种数据结构的两种形式,它们都是一颗完全二叉树,并且满足特定的性质。小顶堆的堆顶元素是最小的元素,而大顶堆的堆顶元素是最大的元素。小顶堆和大顶堆常用于实现优先队列,其操作的时间复杂度通常为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. Mybatis组件介绍

    核心组件 SqlSessionFactoryBuilder SqlSessionFactoryBuilder的作用就是通过XML或者Java代码来建造一个工厂(SqlSessionFactory),并 ...

  2. 解决console控制台反复打印“WebSocket connection to ws://localhost:9528/sockjs-node/107/uadaszgz.websocket failed:Invalid frame header

    element-admin-vue 项目console台一直报websocket连接失败 解决办法 1.vue.config.js中配置devServer.proxy的ws为false  (我没成功) ...

  3. Vue学习之--------事件的基本使用、事件修饰符、键盘事件(2022/7/7)

    文章目录 1.事件处理 1.1. 事件的基本使用 1.1.1 .基础知识 1.1.2. 代码实例 1.1.3.测试效果 1.2.事件修饰符 1.2.1. 基础知识 1.2.2 .代码实例 1.2.3 ...

  4. Linux进程间通信(二)

    信号 信号的概念 信号是Linux进程间通信的最古老的一种方式.信号是软件中断,是一种异步通信的方式.信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某个突发事件. 一旦产生信号 ...

  5. 学习Rust第一天 Rust语言特点

    学习Rust之前,我觉得应该首先了解Rust语言的设计目的是什么?为什么会诞生这门语言?这门语言和其他的语言有什么不同. Rust语言的设计特点 高性能:rust拥有和C++相近的性能表现,所以在嵌入 ...

  6. perl静态变量

    state操作符功能类似于C里面的static修饰符,state关键字将局部变量变得持久. state也是词法变量,所以只在定义该变量的词法作用域中有效,举个例子: #!/usr/bin/perl u ...

  7. 【云原生 · Docker】Docker入门、安装配置

    个人名片: 因为云计算成为了监控工程师‍ 个人博客:念舒_C.ying CSDN主页️:念舒_C.ying Docker入门.安装配置 1. Docker入门简介 2. Docker虚拟化特点 3. ...

  8. 卸载virtualbox中linux虚拟机的增强工具

    报错信息 vboxclient:the virtualbox kernel service is not running 前言 我由virtualbox换到vmware 遇到了这个问题,很烦每次都通知 ...

  9. 如何在 K8S 集群范围使用 imagePullSecret?

    在这篇文章中,我将向你展示如何在 Kubernetes 中使用 imagePullSecrets. imagePullSecrets 简介 Kubernetes 在每个 Pod 或每个 Namespa ...

  10. 一文带你快速入门 Go 语言微服务开发 - Dubbo Go 入门实践总结

    更多详细示例可直接访问 Dubbo 官网 或搜索关注官方微信公众号:Apache Dubbo 1. 安装Go语言环境 建议使用最新版 go 1.17 go version >= go 1.15 ...