基本算法-堆积树排序

简介: 基本算法-堆积树排序

前言

      本文介绍一种排序算法——堆积树排序,是常用的排序算法之一。以下是本篇文章正文内容,包括算法简介、算法特点、算法实现和C++示例。

一、堆积树排序简介

      堆积树排序法是选择排序法的改进版,可以减少在选择排序法中的比较次数,进而减少排序时间。堆积排序法用到了二叉树的技巧,利用堆积树来完成排序。堆积树是一种特殊的二叉树,可分为最大堆积树和最小堆积树两种。


      最大堆积树满足3个条件:


  1. 完全二叉树。
  2. 所有节点的值都大于或等于它左右子节点的值。
  3. 树根是堆积树中最大的。

      最小堆积树满足3个条件:


  1. 完全二叉树。
  2. 所有节点的值都小于或等于它左右子节点的值。
  3. 树根是堆积树中最小的。

      堆积树排序法的实现思路:


  1. 将数列转换为堆积树形式。
  2. 将树根提取出来,剩下的数列继续转换为堆积树;这相当于选择排序法中把最大值或最小值提取出来。
  3. 直到剩下的数列中数据为1,排序结束。


二、算法特点

      1)在所有情况下,时间复杂度均为O(nlog2n)。

      2)堆积排序法不是稳定排序法。

      3)只需要一个额外的空间,空间复杂度为O(1)。

三、代码实现

      代码实现逻辑:

  1. 生成堆积树,提取最值。
  2. 将除最值外的剩余数据生成堆积树,再提取出最值。
  3. 直到所有最值提取完,排序结束。
// 生成最大堆积树
void CreateMaxHeapTree(vector<int> &data, int i, int size)
{
  // j是当前节点的左子树节点,因为是完全二叉树
  int j = 2 * i + 1;
  // temp是当前节点的值
  int temp = data[i];
  // flag标识符用来判断当前节点是否大于左右子树,如果满足条件,可以提前终止循环
  bool flag = false;
  // 二叉树分析
  while (j < size && !flag)
  {
    // j+1是右子树节点,这一步的目的是选出左右子树中最大值和根替换
    if ((j + 1) < size)
    {
      if (data[j] < data[j + 1])
        j++;
    }
    // 如果当前节点大于左右子树了,说明下面的分支都满足堆积树条件,可终止循环
    if (temp >= data[j])
    {
      j -= 1;
      flag = true;
    }
    // 当前节点被更大的子树值替换,继续深入二叉树,找当前节点的合适位置
    else
    {
      data[(j - 1) / 2] = data[j];
      j = 2 * j + 1;
    }
  }
  // 当前节点放置在其合适位置
  data[j / 2] = temp;
}
// 堆积排序
void HeapSort(vector<int>& data)
{
  // 从次最低层开始往上,建立堆积树,初始堆积树的建立很重要,相当于对数据进行一个大致的排序
  int size = int(data.size());
  for (int i = size / 2; i >= 0; --i)
  {
    CreateMaxHeapTree(data, i, size);
  }
  // 扫描n-1次,每次将最值放置在后面,类似选择排序法,但是选择最值的过程是通过二叉树进行的,所以复杂度约为log2n
  for (int i = size - 1; i > 0; --i)
  {
    int temp = data[i];
    data[i] = data[0];
    data[0] = temp;
    CreateMaxHeapTree(data, 0, i);
  }
}

四、C++示例

#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
using namespace std;
// 展示当前顺序
void Show(vector<int> data)
{
  size_t size = data.size();
  for (size_t i = 0; i < size; ++i)
    cout << setw(6) << data[i];
  cout << endl;
}
int Mcount = 1;
// 生成最大堆积树
void CreateMaxHeapTree(vector<int> &data, int i, int size)
{
  // j是当前节点的左子树节点,因为是完全二叉树
  int j = 2 * i + 1;
  // temp是当前节点的值
  int temp = data[i];
  // flag标识符用来判断当前节点是否大于左右子树,如果满足条件,可以提前终止循环
  bool flag = false;
  // 二叉树分析
  while (j < size && !flag)
  {
    // j+1是右子树节点,这一步的目的是选出左右子树中最大值和根替换
    if ((j + 1) < size)
    {
      if (data[j] < data[j + 1])
        j++;
    }
    // 如果当前节点大于左右子树了,说明下面的分支都满足堆积树条件,可终止循环
    if (temp >= data[j])
    {
      j -= 1;
      flag = true;
    }
    // 当前节点被更大的子树值替换,继续深入二叉树,找当前节点的合适位置
    else
    {
      data[(j - 1) / 2] = data[j];
      j = 2 * j + 1;
    }
  }
  // 当前节点放置在其合适位置
  data[j / 2] = temp;
}
// 堆积排序
void HeapSort(vector<int>& data)
{
  cout << "堆积排序:\n原始数据:\n";
  Show(data);
  // 从次最低层开始往上,建立堆积树,初始堆积树的建立很重要,相当于对数据进行一个大致的排序
  int size = int(data.size());
  for (int i = size / 2; i >= 0; --i)
  {
    CreateMaxHeapTree(data, i, size);
  }
  cout << "第"<< Mcount << "次最大堆积树:\n";
  Show(data);
  Mcount++;
  // 扫描n-1次,每次将最值放置在后面,类似选择排序法,但是选择最值的过程是通过二叉树进行的,所以复杂度约为log2n
  for (int i = size - 1; i > 0; --i)
  {
    int temp = data[i];
    data[i] = data[0];
    data[0] = temp;
    CreateMaxHeapTree(data, 0, i);
    cout << "第" << Mcount << "次最大堆积树:\n";
    Show(data);
    Mcount++;
  }
  cout << "排序后结果:\n";
  Show(data);
}
// 主函数
int main()
{
  vector<int> data = { 9,11,567,0,-2,4,2,12,18,6,9,5378,-102,8 };
  // 堆积排序
  HeapSort(data);
  system("pause");
  return 0;
}

     效果图:

      综上所述,堆积排序法巧妙结合了堆积树,是不错的排序算法之一。但是,与其他排序算法相比,不太利于入门学习者理解,我尽可能以通俗的话语阐述,有表达不合理或者代码有问题的地方,烦请提出哦~

      如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

相关文章
|
4月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
106 1
|
4月前
|
算法
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
|
21天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
65 8
|
21天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
59 7
|
21天前
|
算法
树的遍历算法有哪些?
不同的遍历算法适用于不同的应用场景。深度优先搜索常用于搜索、路径查找等问题;广度优先搜索则在图的最短路径、层次相关的问题中较为常用;而二叉搜索树的遍历在数据排序、查找等方面有重要应用。
26 2
|
2月前
|
搜索推荐 Shell
解析排序算法:十大排序方法的工作原理与性能比较
解析排序算法:十大排序方法的工作原理与性能比较
56 9
|
2月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
24 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
2月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
26 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
3月前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
59 2
|
2月前
|
算法
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
32 0