JavaScript大根堆小根堆

序言

最近在刷leetcode遇到一个名为【大根堆】的东西,首先我不太清楚这是什么,所以我不能改厨准确的定义;它是方法?或者一个阶梯思路?或者一个数据结构?或者是其他什么。因为这个名字广泛出现在各种题解中。比如:

既然这么多题出现【大根堆】,它应该很重要,而我又不会;所以我决定搞清楚它

百度百科上说:堆通常是一个可以被看做一棵完全二叉树的数组对象

比如:数组array = [7,3,8,5,1,2]

数组array可以看做如下完全二叉树

          7
        /   \
       3     8
      / \   /
     5   1 2

是要将数组转换成二叉树?
不不不,只是把数组类比成二叉树,要不然我说8和7要调换位置,你会问为什么要调换?

什么是大根堆?

什么是大根堆:每个结点的值都大于等于其左右孩子结点的值
比如:数组array = [7,3,8,5,1,2]
数组array大根堆可以是

          8
        /   \
       5     7
      / \   /
     3   1 2

上述二叉树是不是节点值都大于等于左右子节点?

这就是大根堆;

疑问:你这还是将数组转换为二叉树了呀。
没有啊,数组由[7,3,8,5,1,2] 变成了 [8,5,7,3,1,2]

数组还是数组,只是数组中的值位置发生了改变;

改变的规则是按照二叉树的大根堆规则来的;这样在数组添加,或者删除元素后,可以快速找到数组中最大值,最小值

总结:找最值用大根堆,小根堆

如何构建大根堆呢?

添加

比如:数组array = [7,3,8,5,1,2],二叉树根节点位0;
构建后的大根堆位list = [0],其中0没有实际意义,表示根节点

1、取数组第一位7放入二叉树

          7

list = [0,7]
2、取第2为值3放在二叉树中;

  • list = [0,7,3]
  • list符合大根堆吗?
          7
        /   
       3

符合,不处理
3、取数组array第3为值8放在二叉树中;

  • list = [0,7,3,8]
  • list符合大根堆吗?
          7
        /   \
       3     8
    

不符合,8>7所以8要与7交换位置;

在二叉树视图上比较好观察,但是怎么在程序中交换他们的位置呢?

这里需要补充几个概念;

在完全二叉树中,对于非根节点x都有

父级值在array数组的下标为: Math.floor(x/2)
左子级值在array数组的下标为:2x
右子级值在array数组的下标为: 2
x + 1

          1
        /   \
       2     3
      / \   /  \
     4   5 6    7

所以:7与8交换位子就是8所在的下标3,与7所在的下标1交换位置,所以数组变成了[0,8,3,7]
直观的看二叉树变成了

          8
        /   \
       3     7
    

4、取数组array第4位值5放入list中

  • list = [0,8,3,7,5]
          8
        /   \
       3     7
      /
     5
  • 不符合大根堆条件,因为5 > 3
    如何调整数组呢?将5的位置与3互换;
    5的下标是多少呢?是4;3的下标可以计算出来是2;所以5与3调换后得到list = [0,8,5,7,3]
          8
        /   \
       5     7
      /
     3

总结

通过以上4个步骤,可以知道,在向数组中添加数据的时候,现添加到数组末尾,数组末尾表示数组的叶子节点;将该数据放在叶子节点后,将该值与该值的父级节点值对比,如果该值大于父级节点值,交换两个位置;参考步骤3、4

因为从叶子节点向上交换,所以该值也父级节点交换后,要判断交换后的父级节点与父级的父级节点比较,直至比较到根节点或者小于父级节点;

删除

现在list = [0,8,5,7,3]
与list对应的二叉树如下:

          8
        /   \
       5     7
      /
     3

获取数组最大值,返回array[1]即可;

删除数组最大值呢?

首先:将最后一个节点值与第一个节点子调换;调换后如下
list = [0,3,5,7,8]

          3
        /   \
       5     7
      /
     8

第2步:删除叶子节点;list.pop()
list = [0,3,5,7]

          3
        /   \
       5     7

第3步:重新构建大根堆

在这个例子中,3应该与左子节点交换还是应该去右子节点交换?这个是关键;

假如3与左子节点交换,交换后

          5
        /   \
       3     7

不行吧,还是没构成大根堆,因为7>5
假如3与右子节点交换,交换后

          7
        /   \
       5     3

符合大根堆;

通过上述两个假设,可以到的一个结论,如果根节点与子节点交换,可以重新构成大根堆,则与该子节点交换

了解以上内容,可以编辑代码

手写堆

先搭个架子

class Heap {
   
  constructor(compare) {
   
    this.list = [0] //数组,存放数据
    this.compare =
        typeof compare === 'function' ? compare : this.defaultCompare
  }
  //控制堆升序排列还是降序排列
   defaultCompare(a, b) {
   
   return a > b
  }
  isEmpty() 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值