一、大顶堆

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

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

二、小顶堆

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

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

三、主要区别

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

    文章目录 1.前提条件:确保虚拟机开启.并且连接到redis 2.新建立一个springboot项目,创建项目时勾选web选项 3.在pom中引入redis依赖 4.在application.prop ...

  2. echarts的使用 超好用的报表制作、数据的图形化展示

    地址链接:https://echarts.apache.org/zh/index.html 1.图形选择 2.对应的js代码

  3. 4.pytest结合allure-pytest插件生成allure测试报告

    之前我们使用的测试报告插件是pytest-html 这次使用的插件是allure-pytest,更加美观强大 安装插件 pip3 install allure-pytest 安装allure(Mac) ...

  4. 二叉搜索树 - C++ 实现

    二叉搜索树 - C++ 实现 概述 Overview 二叉查找树(英语:Binary Search Tree, 后文中简称 BST), 也称为二叉搜索树.有序二叉树(ordered binary tr ...

  5. Rust Aya 编写 eBPF 程序

    本文地址:https://www.ebpf.top/post/ebpf_rust_aya 1. 前言 Linux 内核 6.1 版本中有一个非常引人注意的变化:引入了对 Rust 编程语言的支持.Ru ...

  6. [CS61A] Lecture 4. Higher-Order Functions & Project 1: The Game of Hog

    [CS61A] Lecture 4. Higher-Order Functions & Project 1: The Game of Hog Lecture Lecture 4. Higher ...

  7. Spring Cloud Alibaba 从入门到精通(2023)

    Alibaba Cloud 简介 Spring Cloud Alibaba 即 Alibaba Cloud ,基于 Spring Cloud 构建,同时封装了阿里巴巴的 Nacos.Sentinel ...

  8. 2022春每日一题:Day 29

    题目:Mishka and Interesting sum 这题稍微分析就能发现实际这个题就是求区间异或和异或上区间不同数的异或和,因此直接转化为HH的项链. 代码: #include <cst ...

  9. beego-orm-sqlite

    dao.go ``` package daoimport ( "fmt" "github.com/astaxie/beego/orm" "html&q ...

  10. 19、从键盘输入两个数字n,m,求解n,m的最小公倍数

    /* 从键盘输入两个数字n,m,求解n,m的最小公倍数 */ #include <stdio.h> #include <stdlib.h> void getLowsetComM ...