[路飞]js构建堆(优先队列)

204 阅读2分钟

优先队列 - 看是 数组 实际 当成 完全二叉树

记住: 数组第i个元素,他的左节点的对应的元素下标是2 * i + 1,右节点对应的的元素下标是2 * i + 2

反之: 数组第i个元素,他的父节点是 Math.floor((i - 1) / 2)

直接上代码

大顶堆

class Heap {
  constructor() {
    this.data = []
  }
  
  // 堆的大小 === 数组的长度
  size() {
    return this.data.length
  }
  
  swap(i, j) {
    const tmp = this.data[i]
    this.data[i] = this.data[j]
    this.data[j] = tmp
  }
  
  top() {
    if (this.size() === 0) return
    return this.data[0]
  }
  
  // 添加元素
  push(val) {
    this.data.push(val)
    // 需要和父节点的值作对比
    // val 节点的下标
    let index = this.size() - 1
    
    while (index > 0) {
        // 记录父节点的下标
        const parentIndex = Math.floor((index - 1) / 2)
        if (this.data[index] > this.data[parentIndex]) {
            // 交换两个位置的元素
            this.swap(index, parentIndex)
            // 然后再向上对比
            index = parentIndex
        }
    }
  }
  
  // 弹出元素
  pop() {
    // 数组是空的时候
    if (this.size() === 0) return
    const lastOne = this.data.pop()
    this.data[0] = lastOne
    
    // 通过对比向下移动
    // 单前位置
    let index = 0
    
    while(2 * i + 1 < this.size()) {
      // 获取左节点和右节点下标
      const leftIndex = 2 * i + 1
      const rightIndex = 2 * i + 2
      
      // 父节点和 左右节点中最大值进行交换
      let findIndex = index
      
      if (this.data[findIndex] < this.data[leftIndex]) {
        findIndex = leftIndex
      }
      
      // 右节点可能不存在
      if(rightIndex < this.size() && this.data[findIndex] < this.data[rightIndex]) {
        findIndex = rightIndex
      }
      
      // 如果findIndex > index 则代表 单前节点的值不是最小的
      if (findIndex > index) {
        this.swap(index, findIndex)
        index = findIndex
      } else {
        // index没有动 则是当前最大 停止循环
        break
      }
    }
  }
}

这样我们的大顶堆就可以了

小顶堆

父节点一定小于子节点

我们只需要传入一个连个元素对比的方法来判断下就可以了

整理后代码

function defaultCompare(a, b) {
  return a > b
}

class Heap {
  // 给定一个默认的比较方法
  constructor(compare = defaultCompare) {
    this.compare = compare
    this.data = []
  }
  
  // 堆的大小 === 数组的长度
  size() {
    return this.data.length
  }
  
  swap(i, j) {
    const tmp = this.data[i]
    this.data[i] = this.data[j]
    this.data[j] = tmp
  }
  
  top() {
    if (this.size() === 0) return
    return this.data[0]
  }
  
  // 添加元素
  push(val) {
    this.data.push(val)
    // 需要和父节点的值作对比
    // val 节点的下标
    let index = this.size() - 1
    
    while (index > 0) {
        // 记录父节点的下标
        const parentIndex = Math.floor((index - 1) / 2)
        // 这里进行了比较
        if (this.compare(this.data[index], this.data[parentIndex])) {
            // 交换两个位置的元素
            this.swap(index, parentIndex)
            // 然后再向上对比
            index = parentIndex
        }
    }
  }
  
  // 弹出元素
  pop() {
    // 数组是空的时候
    if (this.size() === 0) return
    const lastOne = this.data.pop()
    this.data[0] = lastOne
    
    // 通过对比向下移动
    // 单前位置
    let index = 0
    
    while(2 * i + 1 < this.size()) {
      // 获取左节点和右节点下标
      const leftIndex = 2 * i + 1
      const rightIndex = 2 * i + 2
      
      // 父节点和 左右节点中最大值进行交换
      let findIndex = index
      // 这里进行了比较
      if (!this.compare(this.data[findIndex], this.data[leftIndex])) {
        findIndex = leftIndex
      }
      
      // 右节点可能不存在
      if(rightIndex < this.size() && !this.compare(this.data[findIndex], this.data[rightIndex])) {
        findIndex = rightIndex
      }
      
      // 如果findIndex > index 则代表 单前节点的值不是最小的
      if (findIndex > index) {
        this.swap(index, findIndex)
        index = findIndex
      } else {
        // index没有动 则是当前最大 停止循环
        break
      }
    }
  }
}

总结

  • 可以通过传入compare的比较两只方法来去做顶堆的处理