堆的创建和基本操作

1、堆的概念

如果有一个关键码的集合 K={K0,K1,K2,…,Kn-1},把所有完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i +1 且 Ki <= K2i + 2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

  • 堆中每个节点的值总是不大于或不小于其父节点的值;

  • 堆是一棵完全二叉树

    在这里插入图片描述

2、堆的存储方式

二叉堆是一棵完全二叉树,所以很容易用数组表示,因为一棵高度为 h 的完全二叉树有 2h到 2(h+1)-1个节点,用数组存放一个二叉堆就不会太浪费空间,而且一旦知道高度,就可以知道节点数的范围。而且非常节省存储空间。因为不需要存储左右子节点的指针,单纯通过数组的下标就可以找到一个节点的左右子节点和父节点。

假设i为节点在数组中的下标,则有:
如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

3、堆的创建

将数组"原地"的建成一个堆,不借助辅助空间
方式一:利用在堆中插入元素的思路,尽管数组中包含 n 个元素,可以假设起初堆中只包含一个数据,就是下标为 1 的数据。然后调用插入操纵,将下标从 2 大牌 n 的数据依次插入到堆中,这样就将包含 n 个数据的数组,组织成堆。

方式二:对一组普通的序列向上调整或者向下调整从而形成堆,这个过程可以称为“堆化”(堆化就是顺着节点所在路径,向上或向下对比然后交换)。
在这里插入图片描述
对以下序列{7 5 19 8 4 1 20 13 16} 画图理解向上调整的过程

在这里插入图片描述
在这里插入图片描述
代码实现:


private static void buildHeap(int[] a, int n) {
  for (int i = n/2; i >= 1; --i) {
    heapify(a, n, i);
  }
}

private static void heapify(int[] a, int n, int i) {
  while (true) {
    int maxPos = i;
    if (i*2 <= n && a[i] < a[i*2]) maxPos = i*2;
    if (i*2+1 <= n && a[maxPos] < a[i*2+1]) maxPos = i*2+1;
    if (maxPos == i) break;
    swap(a, i, maxPos);
    i = maxPos;
  }
}
时间复杂度: O(n)

对下标从n/2 开始到 1 的数据进行堆化,下标是 n/2 + 1 到 n的节点是叶子节点,不需要堆化,实际上,对于完全二叉树,下标从 n/2 + 1 到 n的节点都是叶子节点。

4、堆的基本操作

1> 往堆中插入一个元素(注意插入一个元素后要继续满足堆的两个特性)

  • 先将元素放入到底层空间中(注意:空间不够时需要扩容)
  • 将最后新插入的节点向上调整,直到满足堆的性质
    在这里插入图片描述

private static void buildHeap(int[] a, int n) {
  for (int i = n/2; i >= 1; --i) {
    heapify(a, n, i);
  }
}

private static void heapify(int[] a, int n, int i) {
  while (true) {
    int maxPos = i;
    if (i*2 <= n && a[i] < a[i*2]) maxPos = i*2;
    if (i*2+1 <= n && a[maxPos] < a[i*2+1]) maxPos = i*2+1;
    if (maxPos == i) break;
    swap(a, i, maxPos);
    i = maxPos;
  }
}

2> 删除堆顶元素
根据堆的定义我们知道堆顶存储的就是最大值或者最小值

  • 将堆顶元素与堆中最后一个元素交换
  • 将堆中有效数据个数减少一个
  • 对堆顶元素向下调整
    在这里插入图片描述

public void removeMax() {
  if (count == 0) return -1; // 堆中没有数据
  a[1] = a[count];
  --count;
  heapify(a, count, 1);
}

private void heapify(int[] a, int n, int i) { // 自上往下堆化
  while (true) {
    int maxPos = i;
    if (i*2 <= n && a[i] < a[i*2]) maxPos = i*2;
    if (i*2+1 <= n && a[maxPos] < a[i*2+1]) maxPos = i*2+1;
    if (maxPos == i) break;
    swap(a, i, maxPos);
    i = maxPos;
  }
}

注意:容易产生的误区:
假设我们构造的是大顶堆,堆顶元素就是最大的元素。当我们删除堆顶元素之后,就需要把第二大的元素放到堆顶,那第二大元素肯定会出现在左右子节点中。然后我们再迭代地删除第二大节点,以此类推,直到叶子节点被删除。这样最后堆化的堆不满足二叉树特性

在这里插入图片描述

发布了52 篇原创文章 · 获赞 6 · 访问量 1435

猜你喜欢

转载自blog.csdn.net/qq_40488936/article/details/105258457