手撕堆的实现(堆排序,Topk问题)——单手吊打数据结构

简介: 堆的概念与结构🤔前面讲了二叉树的相关概念,堆就是把他的所有元素按照完全二叉树的顺序存储方式存储在一个一维数组中。堆可以用来解决堆排序,topk 问题,以后还会涉及到优先级队列。

堆的概念与结构🤔

前面讲了二叉树的相关概念,堆就是把他的所有元素按照完全二叉树的顺序存储方式存储在一个一维数组中。堆可以用来解决堆排序,topk 问题,以后还会涉及到优先级队列。


堆又分为大堆和小堆,我们把根节点最大的堆叫做大(根)堆,即树中父节点 ≥ 子节点,根节点最小的堆叫做小(根)堆,父节点 ≤ 子节点。


堆的性质:


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

堆总是一棵完全二叉树;

image.png

堆的实现🤔

一般这种实现我们就直接考虑动态版本:


底层结构我们采用的是顺序表的结构,但注意仅仅只是借鉴他的结构,逻辑上他并不是线性表,不应支持头插尾插头删尾删等操作,是不是有了疑问:他的存储结构不就是一个数组吗,为毛不支持啊?原因很简单,要是支持这些操作不就是一个顺序表了嘛,那我干嘛叫堆是吧。

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

向上(向下)调整算法🤔

这里就有一个细节,格局在此提高,我们所谓入堆出堆,都应该时刻维持他作为堆的结构,想象一下我插入一个数后,结果是他有可能是堆有可能不是堆,因为对于父节点的相对大小我并不知道,所以我们实际上插入删除有两步:


插入需要的数据;

对插入后的数据进行调整;

比如给出一个情景:

image.png

这里插入的 4 就很明显的破坏了堆的结构,我们小堆必须保证父节点 ≤ 子节点,那我就要做出调整,我们能用下标表示父节点与子节点的数学关系,让他和父节点进行比较再交换,换完观察现阶段结构是否满足,不满足继续换,我们把这种方法称之为:向上调整算法


堆的删除是删除堆顶的最大或最大值删除后,但依然需要每次调整堆的数据来满足结构,注意这么一个细节,我们删除是在堆顶删除!


栈顶删除后面子节点就会变成父节点,直接破坏了所有父子间关系,所以采用一般的挪动覆盖是不行的,其实我们根本不用抹去这个数,我们只需要把堆顶和堆尾元素交换,删除最后一个数据,然后再向下调整就行了。


首先为了方便,我们单独把交换功能写成一个函数接口;

void Swap(HPDataType* x1, HPDataType* x2)
{
  int tem;
  tem = x1;
  x1 = x2;
  x2 = tem;
}

实现如下:


void AdjustDown(HPDataType* a, int n, int root)
{
  assert(a);
  int parent = root;
  int child = (parent/2)-1;
  while (child < n)
  {
    if (child + 1 < n && a[child] < a[child + 1])
    {
      child++;//找到子节点中较大的一个作为参与调整的子节点
    }
    if (a[child] > a[parent])
    {
      Swap(&a[child],&a[parent]);//不满足小堆就交换
      parent = child;
      child = parent * 2 + 1;
    }
    else
    {
      break;
    }
  }
}

向上调整:


void AdjustUp(HPDataType* a, int child)
{
  assert(a);
  int parent = (child- 1)/2;
  while (child > 0)//不能用parent>=0判断,因为parent始终不小于0
  {
    if (a[parent] > a[child])
    {
      Swap(&a[child], &a[parent]);
      child = parent;
      parent = (child - 1) / 2;
    }
    else
    {
      break;
    }
  }
}


相关文章
|
12天前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
26 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
14天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
57 16
|
1月前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
68 1
|
1月前
|
算法 搜索推荐
数据结构与算法学习十八:堆排序
这篇文章介绍了堆排序是一种通过构建堆数据结构来实现的高效排序算法,具有平均和最坏时间复杂度为O(nlogn)的特点。
70 0
数据结构与算法学习十八:堆排序
|
1月前
|
存储 算法 调度
数据结构--二叉树的顺序实现(堆实现)
数据结构--二叉树的顺序实现(堆实现)
|
1月前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆
|
1月前
|
存储 算法 Java
【用Java学习数据结构系列】用堆实现优先级队列
【用Java学习数据结构系列】用堆实现优先级队列
29 0
|
1月前
|
存储 算法
【数据结构】二叉树——顺序结构——堆及其实现
【数据结构】二叉树——顺序结构——堆及其实现
|
1月前
【数据结构】大根堆和小根堆
【数据结构】大根堆和小根堆
27 0
|
1月前
|
存储 算法 搜索推荐
数据结构--堆的深度解析
数据结构--堆的深度解析

热门文章

最新文章