【数据结构】堆的向上调整和向下调整以及相关方法

简介: 文章目录一、堆的概念二、堆的性质三、堆的分类1.大根堆2.小根堆四、说明五、堆的结构🚩六、堆的向上调整1.图示2.代码实现⌚️3.时间复杂度分析

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤

📃

文章目录

一、堆的概念

堆(Heap)计算机科学中一类特殊的数据结构的统称。如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1, 2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。除了最后一层以外上面的节点但是非空的,最后一层节点是从左到右依次排布的)

二、堆的性质

🔸 非线性,完全二叉树。适合用数组存储。

🔸堆是无序的,也就是左右可以互换

🔸最值总在 0 号位

根据这个特点我们就可以做很多事情,比如TopK问题 (在一堆数据里面找到前 K 个最大 / 最小的数).

比如点餐软件中有上千家店铺,,我想选出该地区好评最多的十家川菜店,我们不用对所有数据排序,只需要取出前 K 个最大 / 最小数据。使用堆排序效率也更高。

三、堆的分类

1.大根堆 2.小根堆

1.大根堆

定义:树中的任意一个双亲节点都大于等于孩子节点。

2.小根堆

定义:树中的任意一个双亲节点都小于等于孩子节点。

四、说明

以下的方法均以小堆来推理,如果想实现大堆,则修改【<】符号等方式实现。

五、堆的结构

typedef int HPDataType;
typedef struct Heap
{
  HPDataType* a;
  int size;
  int capacity;
}HP;

🚩六、堆的向上调整

向上调整的前提是,调整位置之前必须是堆。如果目的是调成小堆,则要保证调整位置之前是小堆;如果目的是调成大堆,则要保证调整位置之前是大堆。

1.图示

2.代码实现

//向上调整
void AdjustUp(HPDataType* a, int child)
{
  //传入数组,child为孩子节点下标
  int parent = (child - 1) / 2;
  //当一直交换到根,停止
  while (child>0)
  {
    if (a[parent] > a[child])
    {
      Swap(&a[parent], &a[child]);
      child = parent;
      parent = (child - 1) / 2;
    }
    else
      return;
  }
}

⌚️3.时间复杂度分析

时间复杂度:O(logN)

最坏情况:调整到根;

最好情况:不用调整,

📌七、堆的向下调整

向下调整的前提是,左右子树必须是小堆或者大堆。

1.思路:

如图:

此案例是要调整根节点40开始向下调整,首先确保根节点的左右子树是小堆(由图得成立)。

1.parent的两个孩子进行比较,选出小的。

2.进行交换

3.child>n结束

2.代码实现

//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
  int child = parent * 2 + 1;
  //一直交换到数的最后,也就是数组的最后一个位置
  while (parent<n)
  {
    if (child + 1 < n && a[child + 1] < a[child])
    {
      child++;
    }
    if (a[child] < a[parent])
    {
      Swap(&a[child], &a[parent]);
      // 继续往下调整
      parent = child;
      child = parent * 2 + 1;
    }
    else
    {
      return;
    }
  }
}

⌚️3.时间复杂度分析

时间复杂度:O(logN)

最坏情况:调整到根;

最好情况:不用调整,

八、删除根

1.思路:

1.先将根与最后一个节点交换,

2.删除最后一个节点;

3.进行向下调整。

2.代码实现

void HeapPop(HP* p)
{
  assert(p);
  assert(p->size > 0);
  Swap(&p->a[0], &p->a[p->size - 1]);
  --p->size;
  AdjustDown(p->a, p->size, 0);
}

⌚️3.时间复杂度分析

时间复杂度:O:N(logN)

九、创建堆

创建堆的思路可以通过向上调整,也可通过向下调整。这里讲通过向上调整建立堆。

由于我的AdjustUp函数是用来调整小堆的,所以,这里创建的也是小堆。

1.思路:

传入参数

a:数组,n:是数组元素个数

1.为p->a开辟n个空间;

2.利用memcpy函数,把数组a复制到p->a中

3.在使用AdjustUp调整,从1-n-1逐步向下延伸;

2.代码实现

//建立小堆
void HeapInitArray(HP* p, int* a, int n)
{
  //a:数组,n:是数组元素个数
  assert(p);
  assert(a);
  p->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
  if (p->a == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  p->size = n;
  p->capacity = n;
  //把传入数组a复制到p->a中
  memcpy(p->a, a, sizeof(HPDataType) * n);
  // 向上调整,调整成一个小堆
  for (int i = 1; i < n; i++)
  {
    AdjustUp(p->a, i);
  }
}

十、所有方法实现汇总

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
//初始化
void HeapInit(HP* p)
{
  assert(p);
  p->a = NULL;
  p->size = 0;
  p->capacity = 0;
}
//销毁
void HeapDestroy(HP* p)
{
  assert(p);
  free(p->a);
  p->a = NULL;
  p->size = p->capacity = 0;
}
//插入数据
void HeapPush(HP* p, HPDataType x)
{
  //从最后一个位置插入
  assert(p);
  //扩容
  if (p->capacity == p->size)
  {
    //如果刚开始数组为空,就开辟4个空间。如果不为空,以后每次扩大2倍。
    int newcapacity = p->capacity==0 ? 4 : p->capacity * 2;
    HPDataType* tmp = (HPDataType*)realloc(p->a, sizeof(HPDataType) * p->capacity);
    if (tmp == NULL)
    {
      perror("realloc fial\n");
      exit(-1);
    }
    p->a = tmp;
    p->capacity = newcapacity;
  }
  p->a[p->size] = x;
  p->size++;
    AdjustUp(p->a, p->size-1);
}
//交换
void Swap(HPDataType* p1, HPDataType* p2)
{
  HPDataType tmp = *p1;
  *p1 = *p2;
  *p2 = tmp;
}
//向上调整
void AdjustUp(HPDataType* a, int child)
{
  //传入数组,child为孩子节点下标
  int parent = (child - 1) / 2;
  //当一直交换到根,停止
  while (child>0)
  {
    if (a[parent] > a[child])
    {
      Swap(&a[parent], &a[child]);
      child = parent;
      parent = (child - 1) / 2;
    }
    else
      return;
  }
}
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
  int child = parent * 2 + 1;
  //一直交换到数的最后,也就是数组的最后一个位置
  while (parent<n)
  {
    if (child + 1 < n && a[child + 1] < a[child])
    {
      child++;
    }
    if (a[child] > a[parent])
    {
      Swap(&a[child], &a[parent]);
      // 继续往下调整
      parent = child;
      child = parent * 2 + 1;
    }
    else
    {
      return;
    }
  }
}
//打印二叉树
void HeapPrint(HP* php)
{
  assert(php);
  for (size_t i = 0; i < php->size; i++)
  {
    printf("%d ", php->a[i]);
  }
  printf("\n");
}
//建立小堆
void HeapInitArray(HP* p, int* a, int n)
{
  //a:数组,n:是数组元素个数
  assert(p);
  assert(a);
  p->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
  if (p->a == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  p->size = n;
  p->capacity = n;
  //把传入数组a复制到p->a中
  memcpy(p->a, a, sizeof(HPDataType) * n);
  // 向上调整,调整成一个小堆
  for (int i = 1; i < n; i++)
  {
    AdjustUp(p->a, i);
  }
}
//删除根
void HeapPop(HP* p)
{
  assert(p);
  assert(p->size > 0);
  Swap(&p->a[0], &p->a[p->size - 1]);
  --p->size;
  AdjustDown(p->a, p->size, 0);
}
//获取根
HPDataType HeapTop(HP* p)
{
  assert(p);
  assert(p->size > 0);
  return p->a[0];
}
//判空
bool HeapEmpty(HP* p)
{
  assert(p);
  return p->size == 0;
}


相关文章
|
12天前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
28 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
14天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
57 16
|
1月前
|
Java C++ 索引
让星星⭐月亮告诉你,LinkedList和ArrayList底层数据结构及方法源码说明
`LinkedList` 和 `ArrayList` 是 Java 中两种常见的列表实现。`LinkedList` 基于双向链表,适合频繁的插入和删除操作,但按索引访问元素效率较低。`ArrayList` 基于动态数组,支持快速随机访问,但在中间位置插入或删除元素时性能较差。两者均实现了 `List` 接口,`LinkedList` 还额外实现了 `Deque` 接口,提供了更多队列操作。
23 3
|
1月前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
68 1
|
1月前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
49 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
2月前
|
存储 Java
【数据结构】优先级队列(堆)从实现到应用详解
本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
52 5
【数据结构】优先级队列(堆)从实现到应用详解
|
1月前
|
存储 编译器 C++
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
|
1月前
|
存储 算法 调度
数据结构--二叉树的顺序实现(堆实现)
数据结构--二叉树的顺序实现(堆实现)
|
1月前
|
存储 算法 分布式数据库
【初阶数据结构】理解堆的特性与应用:深入探索完全二叉树的独特魅力
【初阶数据结构】理解堆的特性与应用:深入探索完全二叉树的独特魅力
|
1月前
|
存储
ES6中的Set数据结构的常用方法和使用场景
ES6中的Set数据结构的常用方法和使用场景