深入浅出堆排序: 高效算法背后的原理与性能

简介: 深入浅出堆排序: 高效算法背后的原理与性能

⛳️ 推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

📋 前言

  🌈堆排序一个基于二叉堆数据结构的排序算法,其稳定性和排序效率在八大排序中也是名列前茅。

  ⛳️堆我们已经讲解完毕了,今天就来深度了解一下堆排序是怎么实现的以及为什么他那么高效。

  📚本期文章收录在《数据结构&算法》,大家有兴趣可以看看呐

  ⛺️ 欢迎铁汁们 ✔️ 点赞 👍 收藏 ⭐留言 📝!

一、堆排序的思想概念

堆排序可以说是排序算法中比较高效的了,既稳定又高效。既然叫堆排序那么肯定离不来堆,基于二叉树来进行构建:

  • 不知道大家发现过没有堆有一个特性
  • 要不就是最大值(大堆)要不然就是一个最小值(小堆)

而我们吧堆顶最大值或最小值进行 pop删除并取出每次的 最大值或者最小值把这些值存储起来

之后他的数据是不是也排序完了,而我们又是用数组来存储的删除不就是把下标 减减吗?

二、堆排序的两种实现方式

堆排序的核心思想就是利用堆的特性来进行数据的取出每次都是最大值或者最小值,那么我得到一组数据要进行堆排序首先:

  • 这组数据需要时堆才能进行排序,那么我们就要开始建堆就完了。

建堆的方法一共有俩种分别是向下取整和向上取整这里都给大家介绍一下

2.1 向上取整

向上取整就是,把新的数据尾插到堆里面然后把他和父节点进行对比调整:

  • 数组存储这里有一个特点 parent = (child-1)/ 2 ;
  • 父节点等于子节点 -1 除二

📚 代码演示:

//向上调整
void adjustup(HeapTypeData* a, int child)
{
  int parent = (child - 1) / 2;
  while (child > 0)
  {
    //建小堆
    if (a[child] < a[parent])
    {
      Swap(&a[child], &a[parent]);
      child = parent;
      parent = (parent - 1) / 2;
    }
    else
    {
      break;
    }
  }
}

2.2 向下取整

向下取整的思想就是把堆顶数据左右子树的的数值进行对比然后向下进行调整:

  • 🔥 向下调整算法有一个前提:左右子树必须是一个堆,才能调整
  • 这里由于是数组存储的所以堆的左右子树都是
  • child = parent* 2+1;
  • 孩子节点的左节点都等于 父节点

    如果堆顶数据和左右子树对比 ,然后再进行调整数据

📚 代码演示:

//向下调整
void adjustdown(HeapTypeData* a, int n, int parent)
{
  int child = parent* 2+1;
  while (child < 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
    {
      break;
    }
  }
}

三、堆排序的实现代码

3.1 如何利用向上调整建堆

向上调整的思想大家都懂了,而建堆的话我们可以这样想:

  • 从数据的第一个数每次向上调整这样
  • 当调整到最后一个数的时候前面所有的都是已经调整好的堆

📚 代码演示:

//向上调整
void adjustup(HeapTypeData* a, int child)
{
  int parent = (child - 1) / 2;
  while (child > 0)
  {
    //建小堆
    if (a[child] > a[parent])
    {
      Swap(&a[child], &a[parent]);
      child = parent;
      parent = (parent - 1) / 2;
    }
    else
    {
      break;
    }
  }
}
//向上调整建堆  OR 建小堆降序 
//                建大堆升序
for (int i = 1; i < n; i++)
{
  adjustup(a, i);
}

3.1 如何利用向下调整建堆

利用向下调整建堆的要求是左右俩边都是堆才可以向下调整:

  • 那么我们可以把他分为 分治子问题 先向下调整左右子树的在一部部调整堆顶
  • 而堆的最后一个子树一定是堆

这样我们就可以利用数组存储堆的特性 父节点等于子节点 -1除2

  • parent = (child-1)/ 2 ;
  • 然后再利用循环 减减 把每个子树都调整完到堆顶堆就减好了

📚 代码演示:

//向下调整
void adjustdown(HeapTypeData* a, int n, int parent)
{
  int child = parent* 2+1;
  while (child < 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
    {
      break;
    }
  }
}
//向上调整建堆  OR 建小堆降序 
//                建大堆升序
for (int i = (n-1-1)/2; i > 0; i--)
{
  adjustdown(a, n, i);
}

3.3 堆建完了如何排序数据

堆我们建完了,排序难道一个个把堆顶数据取出然后再放进去吗? 当然不是排序算法都是在数组的 原本空间上进行排序:

  • 我们的思想还是和删除 POP 一样先把堆顶的数据和堆底进行交换
  • 然后再利用下标减减删除数据,(虚拟删除其实还在)
  • 这样每次最大或者最小的数据都被按规律放在原空间里面了

📚 代码演示:

//开始排序
  int end = n - 1;
  while (end > 0)
  {
    Swap(&a[0], &a[end]);
    adjustdown(a, end, 0);
    end--;
  }

3.4 堆排完整代码

//向上调整
void adjustup(HeapTypeData* a, int child)
{
  int parent = (child - 1) / 2;
  while (child > 0)
  {
    //建小堆
    if (a[child] > a[parent])
    {
      Swap(&a[child], &a[parent]);
      child = parent;
      parent = (parent - 1) / 2;
    }
    else
    {
      break;
    }
  }
}
//向下调整
void adjustdown(HeapTypeData* a, int n, int parent)
{
  int child = parent* 2+1;
  while (child < 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
    {
      break;
    }
  }
}
void HeapSort(int* a, int n)
{
  //向上调整建堆  OR 建小堆降序 
  //                建大堆升序
  /*for (int i = 1; i < n; i++)
  {
    adjustup(a, i);
  }*/
  for (int i = (n-1-1)/2; i > 0; i--)
  {
    adjustdown(a, n, i);
  }
  //开始排序
  int end = n - 1;
  while (end > 0)
  {
    Swap(&a[0], &a[end]);
    adjustdown(a, end, 0);
    end--;
  }

四、俩种实现方式的效率对比

4.1 向上调整建堆时间复杂度计算

4.2 向下调整建堆时间复杂度计算

4.3 对比结果

建堆思想 时间复杂度
向上调整建堆 O(N * logN)
向下调整建堆 O(N)

🔥 所以我们在进行堆排序的时候一定首先选取向下调整算法时间复杂度更优。

  • 假设有1000万个数据
建堆思想 排序次数
向上调整 1000W*24(约等于 2亿多)
向下调整 1000W

所以我们向下调整的算法是远远大于向上调整的这是为什么呢?

  • 🔥 因为 向下调整最后一层节点多且全部需要调整到第一层(调整h-1次)
  • 🔥 而向下调整 最后一层不需要调整,越是接近底层调整越少

4.4 堆的时间复杂度计算

📝文章结语:

☁️ 以上就是本章的全部内容了,各位铁汁们快去试试吧!

看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注

💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖

拜托拜托这个真的很重要!

你们的点赞就是博主更新最大的动力!

有问题可以评论或者私信呢秒回哦。

目录
相关文章
|
2月前
|
数据采集 机器学习/深度学习 算法
|
2月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
66 1
|
13天前
|
缓存 算法 数据处理
时间&空间复杂度,Python 算法的双重考验!如何优雅地平衡两者,打造极致性能?
在Python算法中,时间与空间复杂度的平衡至关重要。时间复杂度反映算法执行时间随输入规模的变化趋势,空间复杂度则关注额外存储空间的需求。优秀的算法需兼顾两者,如线性搜索时间复杂度为O(n),空间复杂度为O(1);二分查找在时间效率上显著提升至O(log n),空间复杂度保持为O(1);动态规划通过牺牲O(n)空间换取O(n)时间内的高效计算。实际应用中,需根据具体需求权衡,如实时数据处理重视时间效率,而嵌入式系统更关注空间节约。通过不断优化,我们能在Python中找到最佳平衡点,实现高性能程序。
34 3
|
24天前
|
机器学习/深度学习 算法 Python
群智能算法:深入解读人工水母算法:原理、实现与应用
近年来,受自然界生物行为启发的优化算法备受关注。人工水母算法(AJSA)模拟水母在海洋中寻找食物的行为,是一种新颖的优化技术。本文详细解读其原理及实现步骤,并提供代码示例,帮助读者理解这一算法。在多模态、非线性优化问题中,AJSA表现出色,具有广泛应用前景。
|
2月前
|
前端开发 算法 JavaScript
React原理之Diff算法
【8月更文挑战第24天】
|
2月前
|
算法 数据安全/隐私保护
基于LS算法的OFDM+QPSK系统信道估计均衡matlab性能仿真
基于MATLAB 2022a的仿真展示了OFDM+QPSK系统中最小二乘(LS)算法的信道估计与均衡效果。OFDM利用多个低速率子载波提高频谱效率,通过循环前缀克服多径衰落。LS算法依据导频符号估计信道参数,进而设计均衡器以恢复数据符号。核心程序实现了OFDM信号处理流程,包括加性高斯白噪声的加入、保护间隔去除、快速傅立叶变换及信道估计与均衡等步骤,并最终计算误码率,验证了算法的有效性。
60 2
|
2月前
|
机器学习/深度学习 算法 搜索推荐
【机器学习】机器学习的基本概念、算法的工作原理、实际应用案例
机器学习是人工智能的一个分支,它使计算机能够在没有明确编程的情况下从数据中学习并改进其性能。机器学习的目标是让计算机自动学习模式和规律,从而能够对未知数据做出预测或决策。
56 2
|
2月前
|
机器学习/深度学习 算法 数据可视化
决策树算法介绍:原理与案例实现
决策树算法介绍:原理与案例实现
|
2月前
|
算法
PID算法原理分析及优化
今天为大家介绍一下经典控制算法之一的PID控制方法。PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。 在大学期间,参加的智能汽车竞赛中就使用到了PID经典控制算法,对于智能小车的调试更加的方便。 一、PID原理 PID控制方法将偏差的比例(proportional)、积分(integral)、微分(derivative)通过线性组合构成控制量,对被控对象进行控制。 常规的PID控制系统如图所示: 系统的输入r(t)为控制量的目标输出值,输出y(t)为控制量的实际输出值,e(t)为输出量目标值与实际值
49 1
|
2月前
|
机器学习/深度学习 运维 算法
深入探索机器学习中的支持向量机(SVM)算法:原理、应用与Python代码示例全面解析
【8月更文挑战第6天】在机器学习领域,支持向量机(SVM)犹如璀璨明珠。它是一种强大的监督学习算法,在分类、回归及异常检测中表现出色。SVM通过在高维空间寻找最大间隔超平面来分隔不同类别的数据,提升模型泛化能力。为处理非线性问题,引入了核函数将数据映射到高维空间。SVM在文本分类、图像识别等多个领域有广泛应用,展现出高度灵活性和适应性。
95 2
下一篇
无影云桌面