关于堆与堆排序

来源:https://loliwithpick.github.io/deny.github.io/2019/03/13/heapTheory/

堆(Heap)

什么是堆

堆是一种特殊的树,它必须满足以下两点要求:

  1. 堆是一个完全二叉树。
  2. 堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值。

每个节点的值都大于等于其子树中每个节点的值的数,叫左**“大顶堆”。每个节点的值都小于等于其子树中每个节点的值的数,叫左“小顶堆”**。

如何实现堆

如何存储堆

用数组储存完全二叉树是非常节省空间的。因为我们不需要存储左右子节点的指针,只要用数组下标,就可以找到一个节点的左右子节点和父节点。

堆支持哪些操作

堆主要的操作有:插入元素和取出堆顶元素。

插入元素

往堆中插入一个元素后,我们需要继续满足堆的特性。如果我们直接把新插入的元素放在堆尾,它就不符合堆的特性了。所以,我们要进行调整,重新满足堆的特性,这一过程叫左**“堆化”**。

而堆化非常简单,就是顺着节点所在路径往上或往下进行对比和交换。堆化有两种方式,一是**“从上往下堆化”,二是“从下往上堆化”**。

“从下往上堆化”

以小顶堆为例,我们让新插入的节点与其父节点进行对比,如果不满足子节点都小于等于父节点的关系,就把它们两个互换。一直重复这个过程,直到父子节点之间满足关系。

“从上往下堆化”

同样以小顶堆为例,**“从上往下堆化”**通常发生在取出栈顶元素之后,我们拿出堆顶元素后,将最后一个元素放到堆顶,然后让该元素与其左右子节点进行对比,如果不满足子节点都小于等于父节点的关系,就把它与不符合要求的子节点交换。一直重复这个过程,直到父子节点之间满足关系。

由于每次我们都是对数组的最后一个元素进行操作,而且堆化进行的都是交换操作,所以不会出现数组"空洞"。

关于堆的代码实现,详细请看:https://loliwithpick.github.io/deny.github.io/2019/03/13/heap/


堆排序(HeapSort)

堆排序是基于堆实现的排序算法,它是一种原地排序算法,时间复杂度为 O(nlogn),由于涉及到数据交换,所以它是不稳定的。

如何实现堆排序

堆排序主要有两个操作:建堆和排序。

建堆

建堆有两种方法:

  1. 借助在堆中插入一个元素的思想。假设数组中的数据存放在下标从 1 到 n 内,堆中只包含一个数据(下标为 1 的数据)。然后调用堆的插入方法,依次把剩下的数据插入到堆中。该方法是从下往上堆化的。
  2. 我们直接对数组进行堆化,而这种操作是从上往下堆化的。我们首先从最后一个非叶子节点开始,分别跟其左右子节点比较和堆化,依次往前。这么说法会很难理解,我们直接从代码方面下手。
/**
 * 以现有数组构建小頂堆
 * @param args
 */
public void BuildMinHeap(int args[]) {
  len = args.length;
  for (int i = len / 2; i >= 0; i--) {
    heapifly(args, i);
  }
}

/**
 * 从上往下堆化
 * @param args
 * @param index
 */
public void heapifly(int args[], int index) {
  while(true) {
    int left = index * 2;
    int right = index * 2 + 1;
    int max = index;

    if (left < len && args[left] > args[index]) {
      max = left;
    }

    if (right < len && args[right] > args[max]) {
      max = right;
    }

    if (max == index) break;

    swap(args, max, index);
    index = max;
  }
}

在完全二叉树中,下标为 n/2 + 1 到 n 的节点是叶子节点。所以我们从下标为 n/2 开始,依次往前进行堆化的。所以我们只需从下标为 n/2 开始,依次往前进行堆化。

排序

假设我们建的是大顶堆,那么建堆后,数组中的数据是按照大顶堆特性进行的。堆顶元素是最大的元素,我们将它与最后一个元素进行交换,那么最大的元素就放在下标为 n 的位置,然后我们将堆长度减一,重复进行前面的操作,最终就得到一个升序排序的数组。

/**
 * 进行排序
 * @param args
 */
public void sort(int args[]) {
  //判断数组是否有效或需要排序
  if (args == null || args.length < 1)
    return;
  BuildMinHeap(args);
  for (int i = args.length - 1; i >= 0; i--) {
    swap(args, 0, i);
    len--;
    heapifly(args, 0);
  }
}

关于堆排序的代码实现,请观看:https://loliwithpick.github.io/deny.github.io/2019/03/13/heapSort/


以上均为个人学习时的笔录总结,若有错误之处,请多多指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值