堆的数据结构

堆是一种特殊的完全二叉树,常用于实现优先级队列。在堆中,父节点的值总是大于或等于其子节点。堆的主要操作包括移除最大值(根节点)和插入新节点。堆排序利用堆的性质,通过不断交换根节点与末尾节点并重新堆化来实现排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概念

Java语言在JVM层面有堆的概念,用来存放new出来的对象。在数据结构层面也有堆的概念,它是一种特殊的完全二叉树。

堆是完全二叉树,也就是说除了树的最后一层节点不需要是满的,其他的每一层从左到右都必须是满的。
在这里插入图片描述
堆是用数组来实现的,堆中每一个节点都满足堆的条件,也就是说每一个关键字的值都大于或等于这个节点的子节点的关键字值。
在这里插入图片描述
堆在存储器中的表示是数组,堆只是一个概念上的表示。

堆是完全二叉树的事实也说明了表示堆的数组中没有空项,即从0–>n-1的每个数据单元都有数据项。

特点

堆的弱序:堆和二叉搜索树相比是弱序的,在二叉搜索树中,当前节点的值总是比左子节点的值大,却比它的右子节点的值小,因此按序遍历相对容易。

堆的组织规则弱,它只要求从根到叶子节点的每一条路径,节点都是按降序排列的。同一节点的左右子节点都没有规律。因此,堆不支持按序遍历,也不能在堆上便利的查找指定关键字,因为在查找的过程中,没有足够的信息决定选择哪个子节点走向下一层。它也不能在少于O(logn)的时间内删除一个指定的节点,因为没有办法找到这个节点。

因此,堆的这种近乎无序的规则似乎毫无用处,不过对于快速移除最大节点的操作,以及快速插入新节点的操作,这种顺序已经足够了。这些操作是使用堆作为优先级队列所需要的全部操作。

操作

1)移除操作:移除最大值,即移除根节点。

步骤:
1.移走根,
2.把左后一个节点移到根的位置
3.一直向下筛选这个节点,直到它在一个大于它的节点之下,小于它的节点之上为止。
在这里插入图片描述
筛选的算法是根节点与比它大的子节点交换位置,但如果根节点比左右子节点都小,与较大的子节点交换位置。

2)堆的插入:数组容量大小增加1,节点插入到数组最后第一个空着的单元中,插入使用向上筛选,如果插入节点比父节点的值大则交换位置。在这里插入图片描述
在这里插入图片描述

数组

用数组表示一颗二叉树,如果某一节点在数组中的索引为x
父节点的索引为 (x-1)/2
左子节点的索引为 2x+1
右子节点的索引为 2x+2

堆排序

堆化

从第一个非叶子节点开始,由下到上对节点进行向下筛选。

两个关键点:
1.第一个非叶子节点:位置为(nums.length-2)/2
2.向下筛选,如果父节点与子节点交换了位置,那么这个新的子节点无法确保比自己的新子孙的值要大,因此需要再次检查向下筛选。

//将一颗完全二叉树堆化(大根堆)
//堆的特点:父节点比左右子节点的值大
//堆化:从最后一个非叶子节点开始,将该节点与子节点进行比较调整,需符合堆的特点
public void heapify(int[] nums, int sz){
    //(sz-2)/2得到的i为第一个非叶子节点
    for(int i = (sz - 2)/2; i >= 0; i--){
        adjust(nums, i, sz);
    }
}
public void adjust(int[] nums, int parent, int len){
    int root = parent;
    int child  = 2*root+1;
    //如果子节点比父节点值大,交换位置
    //如果交换了位置,需要比较交换后的节点是否还满足堆的特点
    while(child < len){
        //得到最大子节点的索引
        if(child+1 < len && nums[child] < nums[child+1]){
            child = child+1;
        }
        if(nums[child] > nums[parent]){
            int temp = nums[parent];
            nums[parent] = nums[child];
            nums[child] = temp;
            parent = child;
            child  = 2*parent+1;
        }else{
            break;
        }
    }
}

交换

上述的过程仅仅是完成了数组的堆化,但正如前文说到的,堆是无法对左右子节点排序的,因此目前整个数组依然是无序的。
但堆有一个特点别忘了,堆的根节点永远是最大值。

利用这个特点,我们可以想出这样一个算法,交换数组的第一个值(根节点)和最后一个值(最后一个叶子节点),交换完成后对最大值前的数组进行堆化,然后再交换新的根节点和新的最后一个节点…递归进行直到可以堆化的数组只有一个节点。

//堆排序:利用根节点永远是最大值的特点
//将根节点与最后一个叶子节点交换位置,再将除最后一个叶子节点外的其余节点进行堆化后,再继续堆排序
  public void heapSort(int[] nums, int start, int len){
      if(start == len){
          return;
      }
      int temp = nums[start];
      nums[start]  = nums[len-1];
      nums[len-1] = temp;
      heapify(nums, len-1);
      heapSort(nums, start, len-1);
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值