(超详解)堆排序+(图解)

news/2024/10/18 7:55:36/

目录:

        1:如何建堆(两种方法)

        2:两种方法建堆的时间复杂度分析与计算

        3:不同类型的排序方式我们应该如何建堆


文章正式开始:

        1:如何建堆

           在实现堆排序之前我们必须得建堆,才能够实现堆排序

                首先在讲解如何建堆之前让我们先来回顾一下堆的概念,堆是一种完全二叉树,它有两种形式,一种是大根堆,另外一种是小根堆。

                大根堆:所有的父亲结点大于或等于孩子结点。

                小根堆:所有的父亲结点小于或等于孩子结点。 

        本文在介绍堆排序的时候我们都默认排升序。

        方法1:我们采用向上调整算法建堆        

                 我们知道向上调整算法的前提是前面的数必须是堆,所以我们就形成了一种思路:

        第一个数我们可以看成是一个堆,那么从第二个数开始我们就依次采用向上调整算法,这样最后我们的数字就会形成一个堆。

        图解:

                

                向上调整建堆的代码如下,如果不理解可以自己尝试画图:

        

                

//假设排升序,建大堆
void HeapSort(int* a, int n){//先建堆,用向上调整算法for (int i = 1; i < n; i++){AdjustUp(a, i);}}

         方法2:采用向下调整的思路建堆

                向下调整的前提:要调整的对象左右子树都得是堆

               那么我们如何通过一个数组来原地建堆呢?

                其实我们可以这样想,叶子结点既可以看作是大堆,也可以看作是小堆,所以我们可以从后面往前面来建堆。

                思路:找到倒数第一个非叶子结点,这样我们可以保证左右子树都是堆,才能够对整个堆使用向下调整算法的思想。

             那么最后一个非叶子结点如何才能找到呢?这里不就是我们要记住的一个特点吗,通过孩子结点来算父亲结点。

                parent=(child-1)/2;

                我们先找到最后一个结点的下标,然后通过结点算父亲的公式不就可以算出来了吗

                所以倒数第一个非叶子结点的下标不就是 (n-1-1)/2吗 ?

                图解过程:

                

 

         2:两种方法建堆复杂度的分析

                首先我们直接公布结论: 

                向上调整算法的时间复杂度为O(N*logN),向下调整算法的时间复杂度为O(N),所以建堆在复杂度的层面来说向下调整算法是优于向上调整算法的。

        向上调整算法的时间复杂度分析:

        我们知道向上调整算法是依次将后一个元素向上进行调整,那么最坏的情况下就是我们所插入一个数就要调整到根节点处。

        

        同理向下调整的复杂度分析

                   

         为啥同样都是建堆的过程,可是为啥向下调整算法的时间复杂度优于向上调整算法呢

        因为向下调整算法时,最后一层结点不需要向下调整,且最后一层的结点比较多,从下往上,结点个数变少,乘以的层数变多,但是主要取决于时间复杂度的是结点个数多的。

        而向上调整算法,最后一层结点的个数多,且需要调整的层数也最高,导致向上调整的时间复杂度高。

3.堆排序

        在讲了前面两种算法的基础上我们就可以来谈一谈我们的堆排序了,堆排序并不是我们所讲的数据结构,虽然说堆数据结构也可以看出堆的升序与降序,但是我们可能并不是只要打印这个数组出来,我们可能还会进行一些算法,比如2分查找....。

        堆排序的思路:

                1:首先对数组进行建堆。

                2:将最后一个元素与第一个元素交换,在向下进行调整

                3:循环往复的进行,最后排除来的就是我们所需要的结果了。

            那我们在排升序的时候应该见建大堆,还是小堆呢?

        相信许多人在看到要排升序的时候,可能第一反应的是建小堆,因为小堆中的第一个数是所有元素中最小的那个数,但是当我们建立小堆的时候,那我们的第二个小的数字如何取呢?

        且当我们将第一个元素排好之后,后面的元素的关系都不对了,就会形成兄弟变父子,父子叔侄变兄弟,那么我们可能还需要建一次堆,那么总体的时间复杂度为N*(N*logN),

        所以我们排升序需要建大堆,排降序需要建小堆。 

        而我们为什么可以这样子做呢?

        我们假设有一个数组我们已近将他建成大堆了,那么我们很明显知道根节点最大,那么我们就可以这样子做。

        将最大的根结点与最后一个数字进行交换,由于我们只是交换了根结点与最后一个元素,其他的结构没有动,所以就可以使用向下调整,然后在对前n-1个元素进行向下调整,整个的时间复杂度为logn。每次选一个大的,我们向将大的排在最后,循环进行就可以排成我们所需要的结果了。

        代码实现

void HeapSort(int* a, int n)
{//向下调整算法建堆,建大堆,排升序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--;}
}

                

        也可以使用向上调整建堆进行堆排序:

        

假设排升序,建大堆
//void HeapSort(int* a, int n)
//{
//	//先建堆,用向上调整算法
//	for (int i = 1; i < n; i++)
//	{
//		AdjustUp(a, i);
//	}
//
//	//将最后一个数与根节点交换
//	//在进行向下调整,循环执行
//	int end = n - 1;
//	/*while (end > 0)
//	{
//		Swap(&a[end], &a[0]);
//		AdjustDown(a, end, 0);
//		--end;
//	}*/
//	
//	
//		
//
//}

        本章完!!!

        感谢观看。


http://www.ppmy.cn/news/1115783.html

相关文章

Vue的第二章节之模版语法(带你感受来自Vue模版语法的魅力)

目录 ​编辑 前言 一、了解模版语法 1. 什么是模版语法 2. 应用场景 3. 对开发的作用 二、插值 1. 文本 2. HTMLj解析 3. 表达式 三、指令 1. v-if/v-else-if/v-else的使用 2. v-show v-show与v-if的区别 3. v-for v-for的使用 扩展&#xff08;下拉框&#x…

SpringCloud OpenFeign--声明式WebService 客户端

&#x1f600;前言 本篇博文是关于SpringCloud OpenFeign的基本介绍&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动…

兽医诊所温湿度失衡,该如何止损?

在现代社会中&#xff0c;宠物已经成为家庭的一员&#xff0c;人们越来越重视宠物的健康和幸福。兽医诊所作为照顾和治疗宠物的重要场所&#xff0c;不仅承担着宠物医疗护理的责任&#xff0c;还肩负着确保宠物在诊所内舒适、安全的任务。 然而&#xff0c;很多时候&#xff0c…

2023年9月30号的火车票什么时候开售?用待办APP提醒抢票

2023年的中秋节和国庆节马上就要到了&#xff0c;你假期的工作都提前完成了吗&#xff1f;今年的国庆节放假时间是从9月29日&#xff08;中秋节&#xff09;-10月6日共8天时间&#xff0c;所以是名副其实的长假了。有不少网友表示自己要在中秋国庆长假期间回家探亲、外出游玩&a…

Vue的进阶使用--模板语法应用

目录 前言 一. Vue的基础语法 1.插值 1.1文本插值 1.2HTML插值 1.3属性插值 1.4Vue演示三元条件运算 2 指令 2.1if&&else指令&#xff08;v-if/v-else-if/v-else&#xff09; 2.2 v-for 指令 2.3 v-on指令(动态参数) 2.4知识点补充之v-if与v-show的区别 3.过…

【操作系统】聊聊进程间通信方式

作为操作系统软件治理的核心 进程&#xff0c;那么进程间通信的方式就非常重要&#xff0c;常见的比如管道、消息队列、共享内存、信号量、信号、Socket等。本篇主要简单介绍下 我们知道每个进程都有自己独立的用户空间&#xff0c;而内核空间是共享的。 管道 ps -ef | gre…

初识软件工程

软件工程是一门涵盖软件开发、维护和管理的学科&#xff0c;它通过应用工程化的原则和方法来提高软件系统的质量和可靠性。在当今数字化和信息化的时代&#xff0c;软件工程对于现代社会的各个领域都具有至关重要的作用。 基本概念&#xff1f; 计算机系统中与硬件相互依存的一…

通过插件去除Kotlin混淆去除 @Metadata标记

在Kotlin中&#xff0c;Metadata是指描述Kotlin类的元数据。它包含了关于类的属性、函数、注解和其他信息的描述。Metadata的作用主要有以下几个方面&#xff1a; 反射&#xff1a;Metadata可以用于在运行时获取类的信息&#xff0c;包括类的名称、属性、函数等。通过反射&…