堆 --typescript

什么是堆?

堆(Heap)是一种特殊的数据结构,是最高效的优先级队列。堆通常是一个可以被看做一棵完全二叉树的数组对象。

  • 堆中某个结点的值总是不大于或不小于其父结点的值;
  • 堆总是一棵完全二叉树。
  • 从根节点到任意结点路径上所有的结点都是有序的

堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。

​ (Ki <= K2i+1 且 Ki<=K2i+2) 或 (Ki >= K2i+1 且 Ki>=K2i+2)

堆的根节点值最大,称为最大堆(大顶堆);堆的根节点值最小,称为最小堆(小顶堆)

知道一个节点index,其左右儿子节点为:index*2+1、index*2+2;其父亲节点为:(index-1)/2 取整

堆的基本操作

  1. 堆的向上调整

    // 向上调整,用于向堆中插入数据
      sift_up(index: number) {
        // index当前位置索引
        while (index) {
          // index父亲索引
          let parent = Math.floor((index - 1) / 2)
          // 满足维护条件,则直接跳出循环
          if (this.compare(parent, index)) break
          // 不满足维护条件,则交换,使之满足维护条件
          this.swap(parent, index)
          // 交换后,该子树的父树发生变化,所以继续向上遍历
          // 直到堆顶,或满足维护条件
          index = parent
        }
      }
    
  2. 堆的向下调整

    // 向下调整,用于取出堆顶数据
      sift_down(index: number) {
        // index开始为堆顶
        // index * 2 + 1左孩子,index * 2 + 2右孩子
        while (index * 2 + 1 < this.size()) {
          let temp = index
          let l = index * 2 + 1
          let r = index * 2 + 2
          // 找到父亲节点,左右儿子中的最值(小根堆为最小值,大根堆为最大值)
          if (this.compare(l, temp)) temp = l
          if (r < this.size() && this.compare(r, temp)) temp = r
          // 若最值是其自身,则堆已维护好跳出循环
          if (temp === index) break
          // 最值不是自身,则与其一个是最值的儿子交换数据
          this.swap(temp, index)
          // 交换后,该树的子树发生变化,所以继续向下遍历
          // 直到堆尾,或满足维护条件
          index = temp
        }
      }
    
  3. 堆的创建

    let heap = new Heap(true)
    Arr.forEach(item => heap.push(item))
    
  4. 堆排序

    // 堆排序 小根堆升序,大根堆降序
      sort() {
        let arr: number[] = []
        while (!this.isEmpty()) {
          arr.push(this.pop()!)
        }
        return arr
      }
    

完整代码

class Heap {
  heap: number[]
  type: boolean
  // 初始化
  constructor(type = true) {
    this.heap = []
    // true为大根堆,false为小根堆
    this.type = type
  }

  // 返回堆的长度
  size() {
    return this.heap.length
  }

  // 判断是否为空堆
  isEmpty() {
    return this.size() ? false : true
  }

  // 返回堆顶元素
  top() {
    if (!this.isEmpty()) throw "Heap is empty"
    return this.heap[0]
  }

  // 添加元素
  push(item: number) {
    // 压入堆尾
    this.heap.push(item)
    // 从堆尾向堆顶维护堆
    this.sift_up(this.size() - 1)
  }

  //  弹出元素
  pop() {
    // 交换堆顶与堆尾元素
    this.swap(0, this.size() - 1)
    // 弹出堆尾元素(即堆顶)
    let item = this.heap.pop()
    // 从堆顶向堆尾维护
    this.sift_down(0)
    return item
  }

  // 堆排序 小根堆升序,大根堆降序
  sort() {
    let arr: number[] = []
    while (!this.isEmpty()) {
      arr.push(this.pop()!)
    }
    return arr
  }

  // 向上调整,用于向堆中插入数据
  sift_up(index: number) {
    // index当前位置索引
    while (index) {
      // index父亲索引
      let parent = Math.floor((index - 1) / 2)
      // 满足维护条件,则直接跳出循环
      if (this.compare(parent, index)) break
      // 不满足维护条件,则交换,使之满足维护条件
      this.swap(parent, index)
      // 交换后,该子树的父树发生变化,所以继续向上遍历
      // 直到堆顶,或满足维护条件
      index = parent
    }
  }

  // 向下调整,用于取出堆顶数据
  sift_down(index: number) {
    // index开始为堆顶
    // index * 2 + 1左孩子,index * 2 + 2右孩子
    while (index * 2 + 1 < this.size()) {
      let temp = index
      let l = index * 2 + 1
      let r = index * 2 + 2
      // 找到父亲节点,左右儿子中的最值(小根堆为最小值,大根堆为最大值)
      if (this.compare(l, temp)) temp = l
      if (r < this.size() && this.compare(r, temp)) temp = r
      // 若最值是其自身,则堆已维护好跳出循环
      if (temp === index) break
      // 最值不是自身,则与其一个是最值的儿子交换数据
      this.swap(temp, index)
      // 交换后,该树的子树发生变化,所以继续向下遍历
      // 直到堆尾,或满足维护条件
      index = temp
    }
  }

  // 比较元素大小
  compare(l: number, r: number) {
    // this.type为true,则为大根堆,反之则为小根堆
    return this.type ? this.heap[l] > this.heap[r] : this.heap[l] < this.heap[r]
  }

  // 交换两元素
  swap(l: number, r: number) {
    [this.heap[l], this.heap[r]] = [this.heap[r], this.heap[l]]
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值