初阶数据结构 堆(二)

简介: 初阶数据结构 堆(二)

一. 堆的接口函数(续)


虽然我们的上一篇博客已经写过了堆的前面一些接口函数


这里我们再重复一遍


1. 结构体形式


我们这里形式上使用一个数组来表示一个逻辑上的二叉树


// 为了方便复用 我们这里将int重定义下
typedef int HeapType;
// 结构体定义一下
typedef struct Heap
{
  HeapType* arr;
  int size;
  int capacity;
}Hp;


2. 初始化和销毁


这里的两个接口函数都很简单 我们直接连起来动手实现一下


初始化


void HeapInit(Hp* obj)
{
  //assert
  assert(obj);
  // main
  obj->arr = NULL;
  obj->size = 0;
  obj->capacity = 0;
}


销毁


void HeapDestroy(Hp* obj)
{
  // assert
  assert(obj);
  // main
  free(obj->arr);
  obj->arr = NULL;
  obj->size = 0;
  obj->capacity = 0;
}


3. 添加数据(小堆为例)


01c08b3069f34895902a37fe7c93f50e.png


void HeapPush(Hp* obj, HeapType x)
{
  // assert
  assert(obj);
  //开辟空间
  if (obj->size==obj->capacity)
  {
    int newcapacity;
    //第一次*2为0
    newcapacity = obj->capacity * 2 + 4;
    //注意这里的sizeof 以及最后的更新capacity
    HeapType* tmp = realloc(obj->arr, sizeof(HeapType) * newcapacity);
    if (tmp=NULL)
    {
      printf("HeapPush realloc error");
      exit(-1);
    }
    else
    {
      obj->arr = tmp;
      obj->capacity = newcapacity;
    }
  }
  //开始插入数据
  obj->arr[obj->size] = x;
  //判断是否需要调整 
}


这里因为判断是否需要调整这个函数要使用很多次


所以说我们单独写出一个函数出来


void HeapAdjust(Hp* obj, int seat)
{
  // assert
  assert(obj);
  //循环判断孩子是否大于父亲 终止条件是孩子等于0(这个时候孩子就是最前面的位置了)或者不需要调整了
  int child = seat;
  int father = (seat - 1) / 2;
  while (child)
  {
    // 小堆 上面的最小 所以说如果孩子小就要向上调整
    if (obj->arr[child]<obj->arr[father])
    {
      HeapType tmp;
      tmp = obj->arr[father];
      obj->arr[father] = obj->arr[child];
      obj->arr[child] = tmp;
      //迭代条件
      child = father;
      father = (child - 1) / 2;
    }
    else
    {
      break;
    }
  }
}


小堆的调整方式如上


整体代码如下


void HeapPush(Hp* obj, HeapType x)
{
  // assert
  assert(obj);
  //开辟空间
  if (obj->size==obj->capacity)
  {
    int newcapacity;
    //第一次*2为0
    newcapacity = obj->capacity * 2 + 4;
    //注意这里的sizeof 以及最后的更新capacity
    HeapType* tmp = realloc(obj->arr, sizeof(HeapType) * newcapacity);
    if (tmp=NULL)
    {
      printf("HeapPush realloc error");
      exit(-1);
    }
    else
    {
      obj->arr = tmp;
      obj->capacity = newcapacity;
    }
  }
  //开始插入数据
  obj->arr[obj->size] = x;
  obj->size++;
  //判断是否需要调整   注意这里不能使用-- 因为这样子使用了就改变size的值了
  HeapAdjust(obj, obj->size - 1);
}


4. 打印数据


这个很简单 直接打印数组的顺序就可以


简单来实现一下


void HeapPrint(Hp* obj)
{
  //assert
  assert(obj);
  //按照数组顺序一个个打印数据就可以 
  int i = 0;
  for ( i = 0; i < obj->size; i++)
  {
    printf("%d ", obj->arr[i]);
  }
}


下面让我们来测试一下


4c70f7cd80f54b258e51c97269a81fc9.png


我们发现出现了一个这样子的错误


到底怎么回事呢?


我们debug看看


这里显示obj->arr是一个空指针


难道是我们前面的tmp写错了?


回去一看


果然


HeapType* tmp = realloc(obj->arr, sizeof(HeapType) * newcapacity);


忘记强制转换类型了


我们强制转换下类型再试试

a535ce741c9845a4b170258c08d227e5.png


我们发现这里程序还是崩溃了


再debug一下我们就发现了


原来是这一行的条件


if (tmp=NULL)


这里要改一下


改成


if (tmp==NULL)


这样子就可以啦


我们再来看看效果

97099bfb5e894774b314a3467f4bf9e5.png


5. 删除数据

bb3eceff54084f6592c24380887c1297.png



我们想要删除堆中的一个数据 还要不改变这个堆的结构 这个时候怎么办呢


这个时候我们这里给出一种很巧妙的解法


我们先将最前面的元素和最后面的元素交换位置


然后再删除掉堆最后面的元素


之后开始向下调整这个堆


如上图所示


下面是删除数据的大体逻辑


void HeapDelete(Hp* obj)
{
  // assert
  assert(obj);
  assert(obj->arr);
  assert(obj->size > 0);
  // 交换头尾元素
  HeapType tmp = obj->arr[0];
  obj->arr[0] = obj->arr[obj->size - 1];
  obj->arr[obj->size - 1] = tmp;
  //删除尾元素
  obj->size--;
  //向下调整
}


这里我们还需要再写一个函数向下调整


void HeapAdjustDown(Hp* obj,int size)
{
  // assert
  assert(obj);
  // set孩子父亲 比较 
  int father = 0;
  int child = 2 * father + 1;
  // 孩子大于等于size的时候结束 
  while (child < size)
  {
    // 右孩子存在 比较两个大小 
    if (child + 1 < size && obj->arr[child + 1] < obj->arr[child])
    {
      child++;
    }
    else
    {
      break;
    }
    //父亲和最大的孩子比较大小 如果大于就交换 如果小于就结束
    if (obj->arr[father] > obj->arr[child])
    {
      HeapType tmp = obj->arr[father];
      obj->arr[father] = obj->arr[child];
      obj->arr[child] = tmp;
      // 交换完迭代
      father = child;
      child = 2 * father + 1;
    }
    else
    {
      break;
    }
  }
}


调整函数如上


我们再来整体看看这个函数


void HeapDelete(Hp* obj)
{
  // assert
  assert(obj);
  assert(obj->arr);
  assert(obj->size > 0);
  // 交换头尾元素
  HeapType tmp = obj->arr[0];
  obj->arr[0] = obj->arr[obj->size - 1];
  obj->arr[obj->size - 1] = tmp;
  //删除尾元素
  obj->size--;
  //向下调整
  HeapAdjustDown(obj,obj->size);
}


测试一下试试

dd69927c2dbe4f64b69d40341a446806.png

3a4005b2f81c4e4cba97b1c29da9f332.png


6. 返回大小


这个很简单 返回size大小


int HeapSize(Hp* obj)
{
  return obj->size;
}


7. 判断为空


bool HeapEmpty(Hp* obj)
{
  return obj->size == 0;
}


以上就是本篇博客的全部内容啦 由于博主才疏学浅 所以难免会出现纰漏 希望大佬们看到错误之后能够


不吝赐教 在评论区或者私信指正 博主一定及时修正


那么大家下期再见咯

相关文章
|
2月前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
43 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
2月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
109 16
|
3月前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
108 1
|
4月前
|
存储 Java
【数据结构】优先级队列(堆)从实现到应用详解
本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
71 5
【数据结构】优先级队列(堆)从实现到应用详解
|
3月前
|
存储 算法 调度
数据结构--二叉树的顺序实现(堆实现)
数据结构--二叉树的顺序实现(堆实现)
|
3月前
|
存储 算法 分布式数据库
【初阶数据结构】理解堆的特性与应用:深入探索完全二叉树的独特魅力
【初阶数据结构】理解堆的特性与应用:深入探索完全二叉树的独特魅力
|
3月前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆
|
3月前
|
存储 算法 Java
【用Java学习数据结构系列】用堆实现优先级队列
【用Java学习数据结构系列】用堆实现优先级队列
38 0
|
3月前
|
存储 算法
【数据结构】二叉树——顺序结构——堆及其实现
【数据结构】二叉树——顺序结构——堆及其实现
|
3月前
【数据结构】大根堆和小根堆
【数据结构】大根堆和小根堆
69 0