我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++超详细分析优化排序算法之堆排序

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++超详细分析优化排序算法之堆排序

堆排序,学习了整整一天才把这个排序彻底搞明白……

首先第一点,堆排序是直接选择排序的一种优化排序算法。由于直接排序算法的遍历次数过多,导致直接排序算法的时间复杂度为O(N^2),不适合排大量数据,堆排序应运而生。

堆排序(Heap Sort)进行的改进是能够保存一部分在每次遍历整个数组找出最大(小)值、次大(小)值,主要利用的就是完全二叉树这种数据结构。(后面说是如何保存这些数据的)

堆排序最重要的知识点无非两个:

1、向下调整算法

2、堆的逻辑结构是一棵完全二叉树

先从定义开始学习:

向下调整算法:顾名思义就是从上到下进行数据的调整,可以将完全二叉树调整为最大堆与最小堆(这两种堆也同时被称为“大顶堆”和“小顶堆”)这种算法的前提是:根节点的左右两棵子树均以建成最大(小)堆。

最大堆:所有的父节点都大于子结点

最小堆:所有的父节点都小于子结点

完全二叉树:从上到下、从左到右依次排列的一种树(即从第一层到第n-1层都是满的,只有第n层不满且从左到右排列数据)

(以建小堆为例)看一种典型的示例:

向下调整算法就是处理这种完全二叉树的一种算法,经过这种算法可将此数组建成最小堆。

先从根节点开始处理:

9 为父(根)节点,0,1都是其子节点,0 < 1;所以将0与9作一次交换;父节点同时下移至子节点,子节点变为新父节点的子节点:(p = parent, c = child)

9 为父(根)节点,2,3都是其子节点,2 < 3;所以将2与9作一次交换;父节点同时下移至子节点,子节点变为新父节点的子节点:

9 为父(根)节点,6 是其子节点,6< 9;所以将6与9作一次交换;父节点同时下移至子节点,子节点变为新父节点的子节点:

发现此时新的子节点已经越界,故停止向下调整;整个堆现已完成建堆成为最小堆!

这便是所谓的“向下调整算法”。

了解了以上知识后,还得知道父节点与子结点的表示方法:

leftchild = parent * 2 + 1;

rightchild = parent * 2 + 2;

parent = (leftchild - 1) /2;

下面代码实战:

//向下调整:
//根节点左右子树必须已经成堆
void AdjustDown(int 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 Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

堆排序的思想现在已经有了雏形:

将一个数组想象成堆,建堆,然后将堆顶最大(小)值置于堆底作为有序数据,这时会新形成一个堆,比之前的堆少一个数据,并且只有根节点的那棵小树未成堆,左右子树已形成大(小)堆,用一次向下调整算法即可将新堆再次建成最大(小)堆。

现在的问题是我们选择建一个最大堆还是最小堆呢?

我们不妨假设建了最小堆,也即上面我们刚刚构建好的堆:

不难发现这样是将最小值筛选出来,再向下调整,选出次小值,这样一来会得到降序的一个数组,反之,若使用最大堆,会得到一个升序的数组。

我们建大堆来得到一个升序数组,现有此无序数组:

//数组
int a[] = { 5,9,6,1,7,2,0,4,3,8 };
//元素个数
int n = (int)sizeof(a) / sizeof(a[0]);

第一步就是建堆:

我们会发现:这样“不听话”的数组显然不符合向下调整算法的前提条件,所以我们可以从这个数组中找能用这个算法的地方:从后向前去调整,最后一个叶子节点?一个数据,不需要调整;

最后一个父节点?他将会有0-2个子节点,而且只有这三个数据,不管怎么“不听话”,这个最小单位会满足“根的左右子树成堆”的这个条件,下一次再将这个父节点-1,即可实现对前一个父节点进行向下调整,循环此步骤直至真正的根节点,这时整个数组会被建成最大堆。

void HeapSort(int a[], int n)
{
    //建堆
	int parent = (n - 1 - 1) / 2;
	while (parent >= 0)
	{
		AdjustDown(a, n, parent);
		parent--;
	}
}

第二步就是排序:

建成堆后,我们需要进行数据的交换形成有序数据区。

void HeapSort(int a[], int n)
{
    //建堆
	int parent = (n - 1 - 1) / 2;
	while (parent >= 0)
	{
		AdjustDown(a, n, parent);
		parent--;
	}
	//已经成最大堆,不用再从最后一个父节点建堆
	//每次只用改变根节点的堆(根左右堆已为最大堆)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

堆排序完毕!

整个代码分享:

#include <stdio.h>
void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
//向下调整:
//根节点左右子树必须已经成堆
void AdjustDown(int 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)
{
	int parent = (n - 1 - 1) / 2;
	while (parent >= 0)
	{
		AdjustDown(a, n, parent);
		parent--;
	}
	//已经成最大堆,不用再从最后一个父节点建堆
	//每次只用改变根节点的堆(根左右堆已为最大堆)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}
void print(int* a, int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
int main()
{
	int a[] = { 5,9,6,1,7,2,0,4,3,8 };
	int n = (int)sizeof(a) / sizeof(a[0]);
	HeapSort(a, n);
	print(a, n);
	return 0;
}

到此这篇关于C++超详细分析优化排序算法之堆排序的文章就介绍到这了,更多相关C++堆排序内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++超详细分析优化排序算法之堆排序

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++超详细分析优化排序算法之堆排序

堆是计算机科学中一类特殊的数据结构的统称,通常是一个可以被看做一棵完全二叉树的数组对象。而堆排序是利用堆这种数据结构所设计的一种排序算法。本文将通过图片详细介绍堆排序,需要的可以参考一下
2023-02-09

浅析经典排序算法之堆排序

堆通常是一个可以被看做一棵树(完全)的数组对象。

分析Java排序算法之希尔排序

这篇文章主要介绍“分析Java排序算法之希尔排序”,在日常操作中,相信很多人在分析Java排序算法之希尔排序问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”分析Java排序算法之希尔排序”的疑惑有所帮助!接下来
2023-06-25

排序算法图解之Java冒泡排序及优化

冒泡排序即通过对待排序的序列从前往后,依次比较相邻元素的值,若发现逆序则交换位置,使较大的元素逐渐移动到后部。本文通过图片和示例介绍了冒泡排序的实现及优化,需要的可以参考一下
2022-11-13

STl中的排序算法详细解析

全排序即把所给定范围所有的元素按照大小关系顺序排列。sort采用的是成熟的"快速排序算法"(目前大部分STL版本已经不是采用简单的快速排序,而是结合内插排序算法)
2022-11-15

排序算法图解之Java快速排序的分步刨析

快速排序是通过一趟排序将要排序的数据分割为独立的两个部分,一部分的所有数据比另外一部分的所有数据要小,然后按照此方法对这两部分分别进行快速排序,整个过程可以递归进行,以此达到整个数据变成有序序列。本文通过示例讲解了快速排序的实现,需要的可以参考一下
2022-11-13

c语言排序算法案例分析

本文小编为大家详细介绍“c语言排序算法案例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“c语言排序算法案例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。在归并算法中,合并两个数列需要消耗m+n的空间,排
2023-06-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录