首页 > 编程语言 >数据结构与算法 -> 大顶堆与小顶堆

数据结构与算法 -> 大顶堆与小顶堆

时间:2023-01-13 11:14:51浏览次数:66  
标签:index return 大顶 索引 let heap 小顶 数据结构 节点

一、大顶堆

  • 大顶堆是一种数据结构,它是一颗完全二叉树,并且满足以下性质:
    • 每个节点的值都大于或等于它的子节点的值
    • 因此,大顶堆的根节点(也称为堆顶)总是最大的元素

二、小顶堆

  • 小顶堆也是一种数据结构,它是一颗完全二叉树,并且满足以下性质:
    • 每个节点的值都小于或等于它的子节点的值
    • 因此,小顶堆的根节点(也称为堆顶)总是最小的元素

三、主要区别

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

标签:index,return,大顶,索引,let,heap,小顶,数据结构,节点
From: https://www.cnblogs.com/P1Kaj1uu/p/17048916.html

相关文章

  • leetcode_数据结构_入门_121. 买卖股票的最佳时机
    121.买卖股票的最佳时机  问题:给定一个数组prices,它的第 i个元素 prices[i]表示一支给定股票第i天的价格。只能选择某一天买入这只股票,并选择在未来的某......
  • 《数据结构 - C语言》单链表
    目录结构定义初始化建立清空求表长判断是否为空表取值查找插入删除销毁遍历打印测试结构定义#include<stdio.h>#include<malloc.h>#include<stdlib.h>#defineOK......
  • 什么是数据结构
    数据结构是一门研究非数值计算的程序设计问题中的操作对象,以及它们之间的关系和操作等相关问题的学科。什么是数据数据是描述客观事物的符号,是计算机中可以操作的对象,......
  • leetcode_数据结构_入门_350. 两个数组的交集 II
    350.两个数组的交集II 给两个整数数组 nums1和nums2,请以数组形式返回两数组的交集(其在交集中出现的次数:等于该数字在两个数组中出现次数的最小值)。返......
  • java 数据结构和栈
          ......
  • pandas——pandas的数据结构与创建数据对象
    1.pandas的数据结构Seriesseries是一维数据importpandasaspds=pd.Series([1,2,3,4,5])print(s.index)#获取series的索引print(s.values)#获取series的值D......
  • leetcode_数据结构_入门_88. 合并两个有序数组
    88.合并两个有序数组问题:两个按非递减顺序排列的整数数组 nums1和nums2,另有两个整数m和n,分别表示nums1和nums2中的元素个数。请合并nums2......
  • Sentinel Go-毫秒级统计数据结构揭秘
    作者:binbin0325背景介绍随着微服务的流行,服务和服务之间的稳定性变得越来越重要。在2020年,Sentinel社区推出SentinelGo版本,朝着云原生方向演进。SentinelGo是一......
  • leetcode_数据结构_入门_1. 两数之和
    1.两数之和问题给定一个整数数组nums 和一个整数目标值target,请在该数组中找出和为目标值target的那 两个整数,并返回它们的数组下标。分析可以假设每种输入......
  • 大话数据结构--图
    图总目录前言七、图7.1图的定义7.1.1各种图的定义有向图无向图完全图稀疏图稠密图网邻接关联(依附)顶点的度路径路径长度回路(环)简单路径简单回路(简单环)连通图(强连通图......