堆排序算法及其稳定性分析

news2024/11/24 16:23:04

堆排序算法及其稳定性分析

什么是堆排序?

堆排序是利用数据结构堆而设计的一种排序算法。

堆分为两种,大顶堆小顶堆

所谓大顶堆就是每个节点的值都大于或者等于其左右孩子节点的值。

小顶堆则是相反的,每个节点的值都小于或者等于其左右孩子节点的值。

下面是一个大顶堆的示例,其拥有下面的性质:

arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]

heapsort1

下面是一个小顶堆的实例,其拥有下面的性质:

arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

heapsort2

大顶堆和小顶堆数据结构是堆排序的基础,下面就看堆排序是如何利用堆来进行排序的。

堆排序的步骤如下:

  • 1.将原始数组转换成一个大顶堆(如果要求升序)或者小顶堆(如果要求降序)。
  • 2.将大顶堆或者小顶堆的首元素与最后一个元素交换
  • 3.剔除尾部元素,将剩下的元素重新构成一个大顶堆或者小顶堆,重复2。

以一个例子来看一下上述过程是怎样的。

原始数组arr = [3,1,4,5,2], 对其进行升序。

步骤1:首先将原数组构建成一个大顶堆。首先从叶子节点开始,将1和5进行对调。

heapsort-demo

步骤2:继续进行调整,将5和3进行对调,此时已经成为了一个大顶堆。

heapsort-demo

步骤3:将堆顶元素5和尾部元素2进行对调。

heapsort-demo

步骤4:重新构建一个大顶堆。将2和4进行对调。

heapsort-demo

步骤5:将堆顶元素4和尾部元素1进行对调。

heapsort-demo

步骤6:重新构建一个大顶堆。将元素1和3进行对调。

heapsort-demo

步骤7:将堆顶元素3和尾部元素2进行对调。

heapsort-demo

步骤8:将堆顶元素2和尾部元素1进行对调。

heapsort-demo

复杂度

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

空间复杂度为: O ( 1 ) O(1) O(1)。没有使用额外的存储空间。

堆排序的代码实现

#include <stdio.h>    
#include <string.h>
#include <ctype.h>      
#include <stdlib.h>   
#include <math.h>  
#include <time.h>

typedef int Status; 


#define MAXSIZE 10000  /* 用于要排序数组个数最大值,可根据需要修改 */
typedef struct
{
	int r[MAXSIZE+1];	/* 用于存储要排序数组,r[0]用作哨兵或临时变量 */
	int length;			/* 用于记录顺序表的长度 */
}SqList;

/* 交换L中数组r的下标为i和j的值 */
void swap(SqList *L,int i,int j) 
{ 
	int temp=L->r[i]; 
	L->r[i]=L->r[j]; 
	L->r[j]=temp; 
}

void print(SqList L)
{
	int i;
	for(i=1;i<L.length;i++)
		printf("%d,",L.r[i]);
	printf("%d",L.r[i]);
	printf("\n");
}


/* 已知L->r[s..m]中记录的关键字除L->r[s]之外均满足堆的定义, */
/* 本函数调整L->r[s]的关键字,使L->r[s..m]成为一个大顶堆 */
void HeapAdjust(SqList *L,int s,int m)
{ 
	int temp,j;
	temp=L->r[s];
	for(j=2*s;j<=m;j*=2) /* 沿关键字较大的孩子结点向下筛选 */
	{
		if(j<m && L->r[j]<L->r[j+1])
			++j; /* j为关键字中较大的记录的下标 */
		if(temp>=L->r[j])
			break; /* rc应插入在位置s上 */
		L->r[s]=L->r[j];
		s=j;
	}
	L->r[s]=temp; /* 插入 */
}

/*  对顺序表L进行堆排序 */
void HeapSort(SqList *L)
{
	int i;
	for(i=L->length/2;i>0;i--) /*  把L中的r构建成一个大根堆 */
		 HeapAdjust(L,i,L->length);

	for(i=L->length;i>1;i--)
	{ 
		 swap(L,1,i); /* 将堆顶记录和当前未经排序子序列的最后一个记录交换 */
		 HeapAdjust(L,1,i-1); /*  将L->r[1..i-1]重新调整为大根堆 */
	}
}

/* **************************************** */


#define N 9
int main()
{
   int i;
   
   /* int d[N]={9,1,5,8,3,7,4,6,2}; */
   int d[N]={50,10,90,30,70,40,80,60,20};
   /* int d[N]={9,8,7,6,5,4,3,2,1}; */

   SqList l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10;
   
   for(i=0;i<N;i++)
     l0.r[i+1]=d[i];
   l0.length=N;
   l1=l2=l3=l4=l5=l6=l7=l8=l9=l10=l0;
   printf("排序前:\n");
   print(l0);
	
   printf("堆排序:\n");
   HeapSort(&l6);
   print(l6);

	return 0;
}

稳定性分析

稳定性就是指对于两个关键字相等的记录,它们在序列中的相对位置,在排序之前和排序之后没有发生改变。通俗地讲就是有两个关键字相等的数据A、B,排序前,A的位置是 i ,B的位置是 j,此时 i < j,则如果在排序后A的位置还是在B之前,那么称它是稳定的。

那么堆排序是一个稳定排序吗?

堆排序的稳定性分析

直接上答案堆排序并不是一个稳定排序。

堆排序的会将原始的数组转化成一个大顶堆或一个小顶堆,在输出堆顶后,此时需要维护堆,操作如下:

(1)堆顶与堆尾交换并删除堆尾,被删除的堆尾的元素就是输出过的元素

(2)把当前堆顶向下调整,直到满足构成堆的条件,重复(1)步骤

在堆顶与堆尾交换的时候两个相等的记录在序列中的相对位置就可能发生改变,这就影响其稳定性了。

下面看一个实际的例子, [5A,6,5B,7,8] ,A和B用于区分相同元素。

数组的原始的状态如下所示:

heapsort-stable1

首先调整下方的子树[6,7,8],将8和6的位置调换。

heapsort-stable2

接着调整下方的子树[5A,8,5B],将8和5A的位置调换。

heapsort-stable3

由于5A和8位置的调换,需要重新调整下方的子树[5A,7,6],将5A和7的位置调换。这个时候5A和5B的顺序就出现了一次乱序。

heapsort-stable4

至此,第一轮排序完毕,将8和数组尾部元素6交换。

heapsort-stable5

接着调整顶部的子树[6,7,5B],将7和6的位置调换。

heapsort-stable6

这个时候第二轮排序已经结束,此时可以将7和数组尾部元素5A进行调整。

heapsort-stable7

接着调整顶部的子树[5A,6,5B],将6和5A的位置调换。

heapsort-stable8

这个时候第三轮排序已经结束,此时可以将6和数组尾部元素5B进行调整。

heapsort-stable9

剩下的元素已经满足了排序的要求,于是直接输出结果。

heapsort-stable10

至此[5A,6,5B,7,8] 排序为 [5B,5A,6,7,8]。可以看到5A和5B的关系发生了变化。

通过这个例子也证明了堆排序不是一个稳定排序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/721472.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

超全整理,接口测试实战详细(实例)一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、接口测试范围 …

Git无法上传删除 Commit里面有大文件

1.Bug描述 因为在一次提交中不小心把一个打包的aab文件弄到commit文件里了&#xff0c;于是在上传的时候push rejected 了。 因为GitHub的文件上限是100M&#xff0c;但是打的包太大了&#xff0c;有170M&#xff0c;所以是不能上传的&#xff0c;但是又是已经在Commit历史中了…

rsync增量备份工具

目录 一、概述 二、配置 rsync 源服务器 1.查看rsync配置文件位置 2.修改 /etc/rsync.conf 配置文件 3.为备份账户创建数据文件 4.保证所有用户对源目录都有读取权限 5.启动 rsync 服务 三、发起端 1.rsync命令 2.将指定的资源远程同步到本地/opt 目录下进行备份。 3.将…

【Docker】docker安装nginx及端口映射相关配置

前言&#xff1a; 最近&#xff0c;在一台新服务器上准备运行一个前端vue项目&#xff0c;服务器上安装了docker&#xff0c;想要尝试试通过docker安装nginx的并运行项目&#xff0c;以下是操作步骤 操作步骤&#xff1a; 一、安装nginx 1、拉取镜像 从docker仓库里拉取最…

unaipp打包app启动界面配置

1、配置代码 2、IOS端启动界面demo参考 iOS平台自定义storyboard启动界面 - DCloud问答

面试官当面夸奖了我,反手却把我挂了,这是什么套路?

最近几个朋友找我聊天&#xff0c;给我讲述了面试过程中遇到的一些不太理解的事情。我有个朋友作为一个技术面试官&#xff0c;今天来分享 10 个面试相关的套路。 1.自我介绍 自我介绍是一个重要的开始&#xff0c;好的开始是成功的一半。不需要太多花里胡哨的东西&#xff0…

SpringBoot05:自动配置原理

一、自动配置原理 SpringBoot官网 1、分析自动配置原理 以HttpEncodingAutoConfiguration&#xff08;Http编码自动配置&#xff09;为例解释自动配置原理 //表示这是一个配置类&#xff0c;和以前编写的配置文件一样&#xff0c;也可以给容器中添加组件 Configuration(prox…

解决阿里qiankun微应用资源无法加载

公司老项目多了&#xff0c;却想用新版本的框架&#xff0c;最好的解决办法就是用微前端。 本文说下我们在用阿里微前端框架qiankun&#xff0c;遇到的一些问题&#xff0c;以及一些巧妙的解决办法。 背景 因为接入微前端很长时间了&#xff0c;导致现在的微应用变成了实际意…

Python接口自动化测试之UnitTest详解

基本概念 UnitTest单元测试框架是受到JUnit的启发&#xff0c;与其他语言中的主流单元测试框架有着相似的风格。其支持测试自动化&#xff0c;配置共享和关机代码测试。支持将测试样例聚合到测试集中&#xff0c;并将测试与报告框架独立。 它分为四个部分test fixture、TestC…

【LeetCode】149. 直线上最多的点数

149. 直线上最多的点数&#xff08;困难&#xff09; 枚举直线 哈希表统计 思路 遍历每两个点之间的连线&#xff0c;然后计算这条连线上有多少个点。 具体步骤如下&#xff1a; 初始化最大点数为 0。遍历每个点&#xff0c;用它和其他点计算斜率。如果两个点的x坐标相同&…

7月大概率加息25bp!美股螺旋式下跌,加密市场“迎难而上”!

今年6月&#xff0c;美股标普500指数走出了自1948年以来最长的熊市&#xff0c;进入新的牛市&#xff0c;美联储暂停加息给全球资本市场一个喘息的机会。尽管如此&#xff0c;美国目前经济基本面的情况仍不及预期&#xff0c;股市其上涨态势恐怕将会迎来一轮调整。 以Solita Ma…

资深开发竟然不清楚int(1)和int(10)的区别

一、困惑 最近遇到个问题&#xff0c;有个表的要加个user_id字段&#xff0c;user_id字段可能很大&#xff0c;于是我提mysql工单alter table xxx ADD user_id int(1)。领导看到我的sql工单&#xff0c;于是说&#xff1a;这int(1)怕是不够用吧&#xff0c;接下来是一通解释。…

做一个小程序需要多少钱

做一个小程序要多少钱&#xff0c;这种分2种类型 定制版 定制版就是按着客户的需求来做了。首先是聊需求&#xff0c;然后画思维导图&#xff0c;做原型图&#xff0c;做完原型图&#xff0c;就是做UI设计&#xff0c;然后做前端&#xff0c;后端。这个费用下来大概几千元到几…

Rust 第五天—代码组织管理

通过之前的内容介绍,对Rust或多或少有了一些了解.也许现在还不能写出“像样子”的项目,但是把大量代码堆积写在一个文件中依旧是不可取的.今天的内容相对轻松一些,聊聊Rust的包和模块 Rust的模块系统可以划分为Package,Crate,Module,具体可以总结如下: Package:整个项目Crate…

【hadoop】Linux安装和配置

安装 RedHat Linux 7.4 创建新的虚拟机 选择“自定义&#xff08;高级&#xff09;” 选择“下一步” 选择“稍后安装操作系统” 选择操作系统的类型 设置虚拟机名称和保存路径 下一步 下一步 设置网络类型&#xff0c;选择“使用仅主机模式网络” 下一步 下一步 下一步 设置硬…

pytorch笔记:归一化

来自B站视频&#xff0c;API查阅&#xff0c;TORCH.NN layer normalization 是针对单个样本&#xff0c;训练和测试的时候行为一致LN 相对于 BN 更适合 RNN&#xff0c;可以降低训练时间LN 中不同样本有不同的归一化参数&#xff0c;以层计算 a 是输入&#xff0c;f 是每层具…

【LeetCode周赛】2022上半年题目精选集——思维题

文章目录 2211. 统计道路上的碰撞次数&#xff08;栈 || 脑筋急转弯&#xff09;解法1&#xff1a;自己想的——使用栈解法2——思维&#xff1a;去掉左右两边往左右开的车代码写法1——找左右端点代码写法2——正则表达式去除流处理api补充&#xff1a;replaceAll() 和 正则表…

VS2022 And QtCreator10 调试 Qt 源码教程

文章目录 背景IDE 调试 Qt 源码Visual Studio 2022Qt Creator 10.0.1 排查思路姊妹篇系列 简 述&#xff1a; 记录使用 Visual Studo 2022 和 QtCreator10 调试 Qt 5.15 源码和 加载 .pdb 的方法。 本文初发于 “偕臧的小站”&#xff0c;同步转载于此。 背景 源码&#xff1a;…

8、动手学深度学习——现代卷积神经网络:AlexNet

1、学习表征 在2012年前&#xff0c;图像特征都是机械地计算出来的。事实上&#xff0c;设计一套新的特征函数、改进结果&#xff0c;并撰写论文是盛极一时的潮流 另一组研究人员&#xff0c;包括Yann LeCun、Geoff Hinton、Yoshua Bengio、Andrew Ng、Shun ichi Amari和Juer…