十大经典排序算法----堆排序(超详细)

这篇具有很好参考价值的文章主要介绍了十大经典排序算法----堆排序(超详细)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. 堆排序的基础知识

1.1 大顶堆&&小顶堆 

1.2 向下调整算法

1.3 物理结构与逻辑结构的关系

2. 堆排序详解

2.1 堆排序整体思路 

2.2 思路详解

2.2.1 建堆

2.2.2 大堆or小堆

2.2.3 输出数据

3. 时间复杂度分析

4. 完整代码

 5. 彩蛋


1. 堆排序的基础知识

堆排序(Heap Sort)就是对直接选择排序的一种改进。此话怎讲呢?直接选择排序在待排序的n个数中进行n-1次比较选出最大或者最小的,但是在选出最大或者最小的数后,并没有对原来的序列进行改变,这使得下一次选数时还需要对全部数据进行比较,效率大大降低。

堆排序算法是Floyd和Williams在1964年共同发明的,同时他们发明了“堆”这种数据结构。

1.1 大顶堆&&小顶堆 

对于待排序的数组元素,我们可以将它的逻辑结构看成二叉树。满足某种条件的这种逻辑结构就是堆啦。

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++

显然,这是一棵完全二叉树。那么某种条件是啥呢?

堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为:大顶堆(大堆);或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆(小堆)。 

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++ 

1.2 向下调整算法

使用该算法的前提是该节点的左右子树要么都是大堆,要么都是小堆。

该算法就是将该节点与左右子树的节点进行比较,重新构建成一个大堆或者小堆。

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++

1.3 物理结构与逻辑结构的关系

假设我们有了一个待排序的数组,并且构建好了他的逻辑结构,怎么能通过孩子找到双亲,或者通过双亲找到左右孩子呢?

先看公式: parent = (child - 1) / 2 ;

                   leftchild = parent * 2 + 1 ;

                   rightchild = parent * 2 + 2 ;

                   rightchild = leftchild + 1;

有了这个公式,左孩子,右孩子,双亲的下标知道一个就可以求其他两个啦!

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++

 文章来源地址https://www.toymoban.com/news/detail-777647.html

2. 堆排序详解

是的,堆排序就是通过建堆的方式来弥补直接选择排序的缺陷滴。 

2.1 堆排序整体思路 

1):给出待排序的数组,咱们脑补一个逻辑结构,然后将该逻辑结构整体调整为大堆或者小堆。 

2): 留个问题:升序是构建大堆还是小堆呢?提示:请参考直接选择排序的缺陷,以及堆排序如何弥补该缺陷的。搞清楚这个问题后排序即可。

3):打印输出数据。

2.2 思路详解

2.2.1 建堆

大堆为例哈:既然前面都铺垫了向下调整算法,你们肯定猜到了是通过该算法来建堆啦。

注意该算法的使用前提:要求左右子树都是大堆。怎么办呢?聪明如你们,我们从逻辑结构的后面往前面用该算法不就行啦!!

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++ 

/// <summary>
/// 交换数组元素
/// </summary>
/// <param name="pa">被交换的元素a</param>
/// <param name="pb">被交换的元素b</param>
void Swap(int* pa, int* pb)
{
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}


/// <summary>
/// 对逻辑结构进行向下调整,构建大堆
/// </summary>
/// <param name="arr">数组首元素地址</param>
/// <param name="n">数组大小</param>
/// <param name="root">要调整的节点</param>
void AdjustDown(int* arr, int n, int root)
{
	//双亲的下标
	int parent = root;
	//较大孩子的下标,默认为左孩子
	int child = parent * 2 + 1;
	//如果孩子的下标不越界,进入循环
	while (child < n)
	{
		//如果右孩子存在(下标没越界),并且右孩子大于左孩子,更新child
		if (child + 1 < n && arr[child + 1] > arr[child])
		{
			child = child + 1;
		}
		//如果较大的孩子大于双亲,交换
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			//改变parent的下标如果满足条件继续向下调整
			parent = child;
			child = parent * 2 + 1;
		}
		//如果较大的孩子不大于双亲,root节点的大堆构建完毕
		else
		{
			break;
		}
	}
}

/// <summary>
/// 堆排序函数
/// </summary>
/// <param name="arr">数组首元素地址</param>
/// <param name="n">数组大小</param>
void HeapSort(int* arr, int n)
{
	//循环从后面往前面对需要的数组元素使用向下调整算法
	int i = 0;
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, n, i);
	}
}

2.2.2 大堆or小堆

以升序为例:建大堆完成后就是这个样子啦:9, 8, 6, 7, 3, 1, 2, 4, 5, 0 ;

                      建小堆完成后就是这个样子:0, 3, 1, 5, 4, 2, 9, 7, 8, 6 ;

建小堆只需要将AdjustDown里面的两个if语句的大于改成小于。

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++

2.2.3 输出数据

这部分比较简单,不多分析。 

3. 时间复杂度分析

堆排序的时间复杂度分析,应该是排序算法中最复杂的,需要具备高中的基础知识!!!!

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++ 

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++ 

4. 完整代码

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>

/// <summary>
/// 交换数组元素
/// </summary>
/// <param name="pa">被交换的元素a</param>
/// <param name="pb">被交换的元素b</param>
void Swap(int* pa, int* pb)
{
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}


/// <summary>
/// 对逻辑结构进行向下调整,构建大堆
/// </summary>
/// <param name="arr">数组首元素地址</param>
/// <param name="n">数组大小</param>
/// <param name="root">要调整的节点</param>
void AdjustDown(int* arr, int n, int root)
{
	//双亲的下标
	int parent = root;
	//较大孩子的下标,默认为左孩子
	int child = parent * 2 + 1;
	//如果孩子的下标不越界,进入循环
	while (child < n)
	{
		//如果右孩子存在(下标没越界),并且右孩子大于左孩子,更新child
		if (child + 1 < n && arr[child + 1] > arr[child])
		{
			child = child + 1;
		}
		//如果较大的孩子大于双亲,交换
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			//改变parent的下标如果满足条件继续向下调整
			parent = child;
			child = parent * 2 + 1;
		}
		//如果较大的孩子不大于双亲,root节点的大堆构建完毕
		else
		{
			break;
		}
	}
}
/// <summary>
/// 打印数组元素
/// </summary>
/// <param name="arr">数组首元素地址</param>
/// <param name="n">数组元素个数</param>
void PrintArray(int* arr, int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
}

/// <summary>
/// 堆排序函数
/// </summary>
/// <param name="arr">数组首元素地址</param>
/// <param name="n">数组大小</param>
void HeapSort(int* arr, int n)
{
	//循环从后面往前面对需要的数组元素使用向下调整算法
	int i = 0;
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, n, i);
	}
	//用来控制向下调整时的数组元素个数
	int end = n - 1;
	while (end > 0)
	{
		//交换元素,获得最大值
		Swap(&arr[0], &arr[end]);
		//进行向下调整, 因为开始end为n-1嘛
		AdjustDown(arr, end, 0);
		//去除较大值
		--end;
	}
}





int main()
{
	//待排序的数组
	int arr[] = { 6,4,2,8,3,1,9,7,5,0 };
	//调用主体函数
	HeapSort(arr, sizeof(arr) / sizeof(arr[0]));
	//打印数据
	PrintArray(arr, sizeof(arr) / sizeof(arr[0]));

	return 0;
}

 5. 彩蛋

为了直观的比较几大排序算法的快慢,我们可以设计程序以毫秒数来量化排序的快慢。

堆排序,十大经典排序算法,排序算法,数据结构,算法,c++ 堆排序,十大经典排序算法,排序算法,数据结构,算法,c++

 

到了这里,关于十大经典排序算法----堆排序(超详细)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《数据结构与算法》之十大基础排序算法

    冒泡排序是一种交换排序,它的思路就是在待排序的数据中,两两比较相邻元素的大小,看是否满足大小顺序的要求,如果满足则不动,如果不满足则让它们互换。 然后继续与下一个相邻元素的比较,一直到一次遍历完成。一次遍历的过程就被成为一次冒泡,一次冒泡的结束

    2024年02月05日
    浏览(99)
  • 数据结构:一篇拿捏十大排序(超详细版)

    排序: 所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性: 假定在待排序的记录序列中,存在多个具有相同的的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前, 而

    2024年02月08日
    浏览(52)
  • 十大经典排序算法----堆排序(超详细)

    目录 1. 堆排序的基础知识 1.1 大顶堆小顶堆  1.2 向下调整算法 1.3 物理结构与逻辑结构的关系 2. 堆排序详解 2.1 堆排序整体思路  2.2 思路详解 2.2.1 建堆 2.2.2 大堆or小堆 2.2.3 输出数据 3. 时间复杂度分析 4. 完整代码  5. 彩蛋 堆排序(Heap Sort)就是对直接选择排序的

    2024年02月03日
    浏览(31)
  • 【数据结构——9大基础排序】一文掌握九大经典排序(配有详细图文说明!!!)

    算法基本思想:(从大到小排序) 在一个 非递减 的有序数组中,要新增一个元素 x ,有两个方法: 1.从数组的头部开始向后遍历,寻找 第一个比x 大 的元素 y ,从y元素开始的所有元素全部向后移动,最后将x元素插入数组。(×) 2.从数组的尾部开始向后向前遍历,寻找 第

    2024年02月08日
    浏览(45)
  • 【数据结构】- 排序(详细介绍几种排序算法!!!*直接插入排序,*希尔排序,*选择排序,*堆排序,*冒泡排序,*快速排序,*归并排序)

    排序无处不在,所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 内部排序 :数据元素全部放在内存中的排序。 外部排序 :数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。 今天

    2024年01月20日
    浏览(62)
  • DSt:数据结构的最强学习路线之数据结构知识讲解与刷题平台、刷题集合、问题为导向的十大类刷题算法(数组和字符串、栈和队列、二叉树、堆实现、图、哈希表、排序和搜索、动态规划/回溯法/递归/贪心/分治)总

    Algorithm:【算法进阶之路】之算法面试刷题集合—数据结构知识和算法刷题及其平台、问题为导向的十大类刷题算法(数组和字符串、链表、栈和队列、二叉树、堆、图、哈希表、排序和搜索、回溯算法、枚举/递归/分治/动态规划/贪心算法)总结 目录 相关文章

    2024年02月08日
    浏览(56)
  • 【数据结构】经典排序法

    欢迎来到Cefler的博客😁 🕌博客主页:那个传说中的man的主页 🏠个人专栏:题目解析 🌎推荐文章:题目大解析2 插入排序是一种简单但有效的排序算法,它的基本思想是将一个元素插入到已排序的序列中,从而得到一个新的有序序列。 就像我们打扑克一样,一张一张的模,

    2024年02月07日
    浏览(37)
  • 【数据结构】经典排序

    什么是排序 :所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性 :假定在待排序的记录序列中,存在多个具有相同的的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之

    2024年02月04日
    浏览(35)
  • 【算法与数据结构】归并排序的代码实现(详细图解)以及master公式的讲解

    目录 1、归并排序  1.1、算法描述  1.2、图解说明 2、代码实现  3、master公式 3.1、公式以及结论 3.2、适用于某些特殊的递归 3.3、计算归并排序的时间复杂度 归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用 递归 或者说是 分治法 (Divide and Conquer)的一个非

    2024年02月08日
    浏览(62)
  • 十大经典排序算法(上)

    目录 1.1冒泡排序 1. 算法步骤  3.什么时候最快 4. 什么时候最慢 5.代码实现 1.2选择排序 1. 算法步骤  2. 动图演示 3.代码实现  1.3 插入排序 1. 算法步骤 2. 动图演示 3. 算法实现 1.4 希尔排序 1. 算法步骤 2. 动图演示  3.代码实现 1.5 归并排序 1. 算法步骤  2. 动图演示  3.代码实现

    2024年02月02日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包