JavaScript数据结构学习笔记(三)

183 阅读2分钟

JavaScript数据结构学习笔记(一)

JavaScript数据结构学习笔记(二)

7. 二叉堆

特点

二叉堆是一种特殊的二叉树,有以下两种特点

  • 结构特性:是一棵完全二叉树,树的根节点/内部节点都有左右侧子节点,且叶子节点尽量在左侧
  • 堆特性:不是最大堆就是最小堆,能够快速导出最大最小值;所有节点都大于等于(最大堆)或小于等于(最小堆)每个它的子节点
  • Array存储数据,但可以想象成一棵树的存储形式

image.png

实现

最小堆

const { Compare, defaultCompare } = require('../utils')

class MinHeap {
  constructor(compareFn = defaultCompare) {
    this.compareFn = compareFn
    this.heap = []
  }
  // 获取左侧子节点index
  getLeftIndex(index) {
    return index * 2 + 1
  }
  // 获取右侧子节点index
  getRightIndex(index) {
    return index * 2 + 2
  }
  // 获取父节点index
  getParentIndex(index) {
    if (index === 0) {
      // 根节点
      return -1
    }
    return Math.floor((index - 1) / 2)
  }
  // 插入节点
  insert(value) {
    if (value) {
      this.heap.push(value)
      // 将插入的最后一个值上移,直到父节点小于这个插入的值
      this.siftUp(this.heap.length - 1)
      return true
    }
    return false
  }
  // 递归上移
  siftUp(index) {
    let parentIndex = this.getParentIndex(index)
    // 父子节点进行比较,如果父节点的值大于该值,则进行上移操作
    while (
      index > 0 &&
      this.compareFn(this.heap[parentIndex], this.heap[index]) ===
        Compare.BIGGER_THAN
    ) {
      // 父子节点值互换
      ;[this.heap[parentIndex], this.heap[index]] = [
        this.heap[index],
        this.heap[parentIndex],
      ]
      // 更新父子节点下标
      index = parentIndex
      parentIndex = this.getParentIndex(parentIndex)
    }
  }
  size() {
    return this.heap.length
  }
  isEmpty() {
    return this.size() === 0
  }
  // 获取最小值
  findMinimum() {
    return this.isEmpty() ? undefined : this.heap[0]
  }
  // 移除最小值并返回
  extract() {
    if (this.isEmpty()) {
      return undefined
    }
    if (this.size() === 1) {
      // 只有一个值,直接返回,不需要下移操作
      return this.heap.shift()
    }
    const removedValue = this.heap.shift()
    // 从根节点开始移除
    this.siftDown(0)
    return removedValue
  }
  // 下移操作(堆化)
  // 实际就是父节点和左右子节点互相比较,值互换的过程
  siftDown(index) {
    let currentIndex = index
    const leftIndex = this.getLeftIndex(index)
    const rightIndex = this.getRightIndex(index)
    const size = this.size()
    // 确保左子节点合法
    // 比较当前节点和左侧子节点的大小
    if (
      leftIndex < size &&
      this.compareFn(this.heap[currentIndex], this.heap[leftIndex]) ===
        Compare.BIGGER_THAN
    ) {
      currentIndex = leftIndex
    }
    // 继续比较右侧
    if (
      rightIndex < size &&
      this.compareFn(this.heap[currentIndex], this.heap[rightIndex]) ===
        Compare.BIGGER_THAN
    ) {
      currentIndex = rightIndex
    }
    // 选择小的那一边交换
    if (index !== currentIndex) {
      ;[this.heap[index], this.heap[currentIndex]] = [
        this.heap[currentIndex],
        this.heap[index],
      ]
      // 递归下移操作
      this.siftDown(currentIndex)
    }
  }
}

最大堆

最大堆就是最小堆的逆向运算,只需要改变compareFn的比较方式即可

const { defaultCompare } = require('../utils')
const { MinHeap } = require('./minHeap')

function reverseCompare(compareFn) {
  return (a, b) => compareFn(b, a)
}

class MaxHeap extends MinHeap {
  constructor(compareFn = reverseCompare(defaultCompare)) {
    super(compareFn)
  }
}