自定义 Heap 数据结构【JS】

什么是堆

简单的解释是,就是一个全二叉树或者贴近全二叉树,它的节点的子节点都比自己小或者都比自己大。也就是说,堆有个特点:最上边的节点(堆顶)不是最大的,就是最小的,看自己添加元素的时候采用的什么形式。

原来堆就是这么整的。十几年前学的,我居然一点儿印象也没有了。。。

简单实现

接口主要是:

  1. constructor() // 用数组初始化,默认排序是数字升序
  2. add() // 添加一个并排好序
  3. peek() // 查询顶部元素
  4. poll() // 删除顶部元素并返回其值
class Heap {
  constructor(data, comparator = (a, b) => a - b) {
    this.data = []
    this.comparator = comparator
    for (let item of data) this.add(item)
  }
  size() { return this.data.length }
  swap(index1, index2) {
    [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]]
  }
  peek() {
    return this.data[0]
  }
  add(item) {
    this.data.push(item)
    this.heapifyUp()
  }
  poll() {
    this.swap(0, this.size()-1)
    const top = this.data.pop()
    this.heapifyDown()
    return top
  }
  heapifyUp() {
    let index = this.size() - 1
    while (this.hasParent(index) && this.comparator(this.data[index], this.getParent(index)) < 0) {
      this.swap(index, this.getParentIndex(index))
      index = this.getParentIndex(index)
    }
  }
  heapifyDown() {
    let index = 0
    while (this.hasLeftChild(index)) {
      let smallerChildIndex = this.getLeftChildIndex(index)
      if (this.hasRightChild(index) && this.comparator(this.getRightChild(index), this.getLeftChild(index)) < 0) {
        smallerChildIndex = this.getRightChildIndex(index)
      }
      if (this.comparator(this.data[index], this.data[smallerChildIndex]) < 0)
        break
      else {
        this.swap(index, smallerChildIndex)
        index = smallerChildIndex
      }
    }
  }

  getParent(index) { return this.data[this.getParentIndex(index)] }
  getLeftChild(index) { return this.data[this.getLeftChildIndex(index)] }
  getRightChild(index) { return this.data[this.getRightChildIndex(index)] }

  hasParent(index) { return this.getParentIndex(index) >= 0 }
  hasLeftChild(index) { return this.getLeftChildIndex(index) < this.size() }
  hasRightChild(index) { return this.getRightChildIndex(index) < this.size() }

  getParentIndex(index) { return Math.floor((index - 1) / 2) }
  getLeftChildIndex(index) { return index * 2 + 1 }
  getRightChildIndex(index) { return index * 2 + 2 }
}

完整代码

<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Heap II</title>
  <link href="https://code.jquery.com/qunit/qunit-git.css" rel="stylesheet" type="text/css"/>
  <script src="https://code.jquery.com/qunit/qunit-git.js"></script>
</head>
<body>

<div id="qunit"></div>
<div id="qunit-fixture"></div>

<script>

  /**
   * https://www.youtube.com/watch?v=t0Cq6tVNRBA
   */
  class Heap {
    constructor(data, comparator = (a, b) => a - b) {
      this.data = []
      this.comparator = comparator
      for (let item of data) this.add(item)
    }
    size() { return this.data.length }
    swap(index1, index2) {
      [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]]
    }
    peek() {
      return this.data[0]
    }
    add(item) {
      this.data.push(item)
      this.heapifyUp()
    }
    poll() {
      this.swap(0, this.size()-1)
      const top = this.data.pop()
      this.heapifyDown()
      return top
    }
    heapifyUp() {
      let index = this.size() - 1
      while (this.hasParent(index) && this.comparator(this.data[index], this.getParent(index)) < 0) {
        this.swap(index, this.getParentIndex(index))
        index = this.getParentIndex(index)
      }
    }
    heapifyDown() {
      let index = 0
      while (this.hasLeftChild(index)) {
        let smallerChildIndex = this.getLeftChildIndex(index)
        if (this.hasRightChild(index) && this.comparator(this.getRightChild(index), this.getLeftChild(index)) < 0) {
          smallerChildIndex = this.getRightChildIndex(index)
        }
        if (this.comparator(this.data[index], this.data[smallerChildIndex]) < 0)
          break
        else {
          this.swap(index, smallerChildIndex)
          index = smallerChildIndex
        }
      }
    }

    getParent(index) { return this.data[this.getParentIndex(index)] }
    getLeftChild(index) { return this.data[this.getLeftChildIndex(index)] }
    getRightChild(index) { return this.data[this.getRightChildIndex(index)] }

    hasParent(index) { return this.getParentIndex(index) >= 0 }
    hasLeftChild(index) { return this.getLeftChildIndex(index) < this.size() }
    hasRightChild(index) { return this.getRightChildIndex(index) < this.size() }

    getParentIndex(index) { return Math.floor((index - 1) / 2) }
    getLeftChildIndex(index) { return index * 2 + 1 }
    getRightChildIndex(index) { return index * 2 + 2 }
  }


  QUnit.module('Heap II', function () {
    QUnit.test('ESC / TopMin', function (assert) {
      const heap = new Heap([5, 4, 6])
      console.log(JSON.stringify(heap.data))
      assert.deepEqual(heap.data, [4, 5, 6])
      assert.equal(heap.peek(), 4)
      heap.add(1)
      heap.add(3)
      assert.equal(heap.peek(), 1)
      assert.deepEqual(heap.data, [1, 3, 6, 5, 4])
      assert.equal(heap.poll(), 1)
      assert.deepEqual(heap.data, [3, 4, 6, 5])
    });

    QUnit.test('DESC / TopMax', function (assert) {
      const heap = new Heap([5, 4, 6], (a, b) => b - a)
      assert.deepEqual(heap.data, [6, 4, 5])
      assert.equal(heap.peek(), 6)
      heap.add(1)
      assert.deepEqual(heap.data, [6, 4, 5, 1])
      heap.add(7)
      assert.equal(heap.peek(), 7)
      assert.deepEqual(heap.data, [7, 6, 5, 1, 4])
      assert.equal(heap.poll(), 7)
      assert.deepEqual(heap.data, [6, 4, 5, 1])
    });
  });
</script>

</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值