【二叉树的顺序结构:堆 && 堆排序 && TopK](一)

简介: 【二叉树的顺序结构:堆 && 堆排序 && TopK](一)

1 二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。 现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

af72ea4820d44e79858b80909ce6d366.png

2 堆的概念及结构

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

堆的性质:

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

堆总是一棵完全二叉树

3 堆的实现

3.1 堆向下调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提: 左右子树必须是一个堆,才能调整。

int array [] = { 27 , 15 , 19 , 18 , 28 , 34 , 65 , 49 , 25 , 37 };

a758eed96bee4be3b80890326c6d0cb4.png

具体代码:

void AdjustDown(int* a, int parent, int sz)
{
  assert(a);
  int child = parent * 2 + 1;
  while (child < sz)
  {
    if (child + 1 < sz && a[child + 1] > a[child])//建立小堆 a[child + 1] < a[child]
      child++;
    if (a[child] > a[parent])//建立小堆 <
    {
      Swap(&a[child], &a[parent]);
      parent = child;
      child = parent * 2 + 1;
    }
    else
      break;
  }
}

这里建立的是大堆,建立小堆代码中我给了注释.

3.2 堆向上调整算法

堆的向上调整算法往往与push相搭配,push完一个数据就将该数据向上调整,这样就能够保证堆的结构不会被破坏。

image.png

代码实现:

void AdjustUp(int* a, int child)
{
  assert(a);
  int parent = (child - 1) / 2;
  while (child>0)//用parent>=0也行,只是这样的话就不是正常结束的了
  {
    if (a[child] > a[parent])//建小堆 <
    {
      Swap(&a[child], &a[parent]);
      child = parent;
      parent = (child - 1) / 2;
    }
    else
      break;
  }
}

我们不难发现一个数据向上调整或者向下调整的时间复杂度都为logN.

3.3堆的插入

69e8c3f09a21413984f76c54d486934c.png

代码实现:

void HeapPush(Heap* php, HeapDataType x)
{
  assert(php);
  if (php->capacity == php->sz)
  {
    int newcapacity = php->a == NULL ? 4 : php->capacity * 2;
    HeapDataType* tmp = (HeapDataType*)realloc(php->a, sizeof(HeapDataType) * newcapacity);
    if (tmp == NULL)
    {
      perror("realloc fail:");
      exit(-1);
    }
    php->a = tmp;
    php->capacity = newcapacity;
  }
  php->a[php->sz] = x;
  php->sz++;
  //向上调整算法,保证建立的是堆(这里以建小堆为例)
  AdjustUp(php->a, php->sz - 1);//第二个参数传的是push这个数据的下标
}

3.4 堆的删除

假设建小堆,要pop掉最小的一个数值(堆顶),要让下面的结构继续保持小堆结构就不能只将数据向前挪动一位,否则堆的结构将会被破坏。正确做法是将堆顶的数据与最后一个数据交换,然后重新向下建堆,再pop掉堆尾数据。

image.png

代码实现:

void HeapPop(Heap* php)
{
  assert(php);
  assert(php->sz > 0);
  //假设建小堆,要pop掉最小的一个数值(堆顶),要让下面的结构继续保持小堆结构就不能只将数据向前挪动一位,
  //否则堆的结构将会被破坏。正确做法是将堆顶的数据与最后一个数据交换,然后重新向下建堆,再pop掉堆尾数据。
  Swap(&php->a[0], &php->a[php->sz - 1]);
  php->sz--;
  AdjustDown(php->a, 0, php->sz);
}


目录
相关文章
|
1月前
|
存储 算法 索引
二叉树的顺序结构(堆的实现)
二叉树的顺序结构(堆的实现)
14 1
|
6月前
【数据结构】二叉树-堆(top-k问题,堆排序,时间复杂度)
【数据结构】二叉树-堆(top-k问题,堆排序,时间复杂度)
52 4
|
5月前
|
测试技术
深入理解数据结构第二弹——二叉树(2)——堆排序及其时间复杂度
深入理解数据结构第二弹——二叉树(2)——堆排序及其时间复杂度
58 0
|
6月前
【完全二叉树魔法:顺序结构实现堆的奇象】(下)
【完全二叉树魔法:顺序结构实现堆的奇象】
|
6月前
|
算法
【完全二叉树魔法:顺序结构实现堆的奇象】(中)
【完全二叉树魔法:顺序结构实现堆的奇象】
|
6月前
|
算法
二叉树顺序结构&堆实现
二叉树顺序结构&堆实现
45 0
|
6月前
|
算法 C语言
堆的应用:堆排序
堆的应用:堆排序
96 0
|
6月前
|
存储 算法 搜索推荐
【完全二叉树魔法:顺序结构实现堆的奇象】(上)
【完全二叉树魔法:顺序结构实现堆的奇象】
|
存储 Windows
二叉树,堆排序及TopK问题
二叉树,堆排序及TopK问题
49 0