js 递归查询所有的叶子结点_JS实现堆排序

63b9d860d89e87df5749b962e848b922.png

堆的预备知识

  • 堆是一个完全二叉树。

  • 完全二叉树:二叉树除开最后一层,其他层结点数都达到最大,最后一层的所有结点都集中在左边(左边结点排列满的情况下,右边才能缺失结点)。

  • 大顶堆:根结点为最大值,每个结点的值大于或等于其孩子结点的值。

  • 小顶堆:根结点为最小值,每个结点的值小于或等于其孩子结点的值。

  • 堆的存储:堆由数组来实现,相当于对二叉树做层序遍历。如下图:

eea40821d10c475a28aa9f7146df9d5b.png

a889d8d7f0f4367cf5848e6e2db9071e.png

对于结点 i ,其子结点为 2i+1 与 2i+2 。

堆排序算法

24d1e7a3703349d3443b1f315514c700.png

现在需要对如上二叉树做升序排序,总共分为三步:

  1. 将初始二叉树转化为大顶堆(heapify),此时根结点为最大值,将其与最后一个结点交换。

  2. 除开最后一个结点,将其余节点组成的新堆转化为大顶堆,此时根结点为次最大值,将其与最后一个结点交换。

  3. 重复步骤2,直到堆中元素个数为1(或其对应数组的长度为1),排序完成。

下面详细图解这个过程:

步骤1:

初始化大顶堆,首先选取最后一个非叶子结点(我们只需要调整父节点和孩子节点之间的大小关系,叶子结点之间的大小关系无需调整)。设数组为arr,则第一个非叶子结点的下标为:i = Math.floor(arr.length/2 - 1) = 1,也就是数字4,如图中虚线框,找到三个数字的最大值,与父节点交换。

4c8061ab88e58e599b42a67957ff47dd.png

然后,下标 i 依次减1(即从第一个非叶子结点开始,从右至左,从下至上遍历所有非叶子节点)。后面的每一次调整都是如此:找到父子结点中的最大值,做交换。

7f1e8491aea77754c3bd691278d926c1.png

这一步中数字6、1交换后,数字[1,5,4]组成的堆顺序不对,需要执行一步调整。因此需要注意,每一次对一个非叶子结点做调整后,都要观察是否会影响子堆顺序!

de102a5360f8897723ebbf5b8befa5e4.png

根节点与最后一个结点交换

这次调整后,根节点为最大值,形成了一个大顶堆,将根节点与最后一个结点交换。

步骤2:

除开当前最后一个结点6(即最大值),将其余结点[4,5,3,1]组成新堆转化为大顶堆(注意观察,此时根节点以外的其他结点,都满足大顶堆的特征,所以可以从根节点4开始调整,即找到4应该处于的位置即可)。

22212a2e85f6142fb9132a5d0582dd14.png

6d032e251aa26907e6174aae874362dc.png

根节点与最后一个结点交换

步骤3:

接下来反复执行步骤2,直到堆中元素个数为1:

afcdc958fc467382d10c9e71478076f4.png

ac4082bfbcab7c8eb0f2c8af6735c29f.png

c364d77fb78a665885fd913ef446eb41.png

堆中元素个数为1, 排序完成。

JavaScript实现

// 交换两个节点function swap(A, i, j) {  let temp = A[i];  A[i] = A[j];  A[j] = temp; }// 将 i 结点以下的堆整理为大顶堆,注意这一步实现的基础实际上是:// 假设 结点 i 以下的子堆已经是一个大顶堆,adjustheap 函数实现的// 功能是实际上是:找到 结点 i 在包括结点 i 的堆中的正确位置。后面// 将写一个 for 循环,从第一个非叶子结点开始,对每一个非叶子结点// 都执行 adjustheap 操作,所以就满足了结点 i 以下的子堆已经是一大//顶堆function adjustHeap(A, i, length) {  let temp = A[i]; // 当前父节点// j  for(let j = 2*i+1; j2*j+    temp = A[i];  // 将 A[i] 取出,整个过程相当于找到 A[i] 应处于的位置    if(j+1 < length && A[j] < A[j+1]) {       j++;   // 找到两个孩子中较大的一个,再与父节点比较    }    if(temp < A[j]) {      swap(A, i, j) // 如果父节点小于子节点:交换;否则跳出      i = j;  // 交换后,temp 的下标变为 j    } else {      break;    }  }}// 堆排序function heapSort(A) {  // 初始化大顶堆,从第一个非叶子结点开始  for(let i = Math.floor(A.length/2-1); i>=0; i--) {    adjustHeap(A, i, A.length);  }  // 排序,每一次for循环找出一个当前最大值,数组长度减一  for(let i = Math.floor(A.length-1); i>0; i--) {    swap(A, 0, i); // 根节点与最后一个节点交换    adjustHeap(A, 0, i); // 从根节点开始调整,并且最后一个结点已经为当                         // 前最大值,不需要再参与比较,所以第三个参数                         // 为 i,即比较到最后一个结点前一个即可  }}let Arr = [4, 6, 8, 5, 9, 1, 2, 5, 3, 2];heapSort(Arr);alert(Arr);


程序注释:将 i 结点以下的堆整理为大顶堆,注意这一步实现的基础实际上是:假设 结点 i 以下的子堆已经是一个大顶堆,adjustHeap 函数实现的功能是实际上是:找到 结点 i 在包括结点 i 的堆中的正确位置。后面做第一次堆化时,heapSort 中写了一个 for 循环,从第一个非叶子结点开始,对每一个非叶子结点都执行 adjustHeap 操作,所以就满足了每一次 adjustHeap 中,结点 i 以下的子堆已经是一大顶堆。

复杂度分析:adjustHeap 函数中相当于堆的每一层只遍历一个结点,因为
具有n个结点的完全二叉树的深度为[log2n]+1,所以 adjustHeap 的复杂度为 O(logn),而外层循环共有 f(n) 次,所以最终的复杂度为 O(nlogn)。

堆的应用

堆主要是用来实现优先队列,下面是优先队列的应用示例:

  • 操作系统动态选择优先级最高的任务执行。

  • 静态问题中,在N个元素中选出前M名,使用排序的复杂度:O(NlogN),使用优先队列的复杂度: O(NlogM)。

而实现优先队列采用普通数组、顺序数组和堆的不同复杂度如下:

06639460840b283e2d1d9612aba58fcc.png

使用堆来实现优先队列,可以使入队和出队的复杂度都很低。


作者:Leondt
链接:https://www.jianshu.com/p/90bf2dcd6a7b

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用MyBatis的递归查询实现树形结构的查询。具体步骤如下: 1. 定义实体类 定义一个实体类,表示树的节点,包含节点id、父节点id、节点名称等属性。 2. 定义Mapper接口 定义一个Mapper接口,包含一个方法,用于查询树形结构的节点。方法的返回值为List集合,表示查询到的树形结构的节点。 3. 编写Mapper.xml配置文件 在Mapper.xml配置文件中,编写递归查询语句,通过查询语句实现树形结构的查询。具体实现方式如下: - 定义一个select语句,用于查询指定节点的所有子节点。 - 在select语句中,使用union all关键字连接多个子查询语句,实现递归查询。 - 在子查询语句中,使用with recursive关键字定义递归查询语句。 4. 调用Mapper接口 在Java代码中,调用Mapper接口的方法,获取查询到的树形结构的节点。可以通过递归遍历节点,实现树形结构的展示。 下面是一个示例代码,供参考: ``` // 定义实体类 public class TreeNode { private Integer id; private Integer parentId; private String name; // getter和setter方法 } // 定义Mapper接口 public interface TreeNodeMapper { List<TreeNode> selectTreeNodes(Integer parentId); } // 编写Mapper.xml配置文件 <select id="selectTreeNodes" parameterType="java.lang.Integer" resultType="TreeNode"> with recursive cte(id, parent_id, name) as ( select id, parent_id, name from tree_node where parent_id = #{parentId} union all select tn.id, tn.parent_id, tn.name from tree_node tn inner join cte on tn.parent_id = cte.id ) select * from cte; </select> // 调用Mapper接口 @Autowired private TreeNodeMapper treeNodeMapper; public List<TreeNode> getTreeNodes(Integer parentId) { return treeNodeMapper.selectTreeNodes(parentId); } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值