堆的实现,以及画图和代码分析,建堆,堆排序,时间复杂度的分析

news2024/10/6 2:29:37

堆的实现

  • 堆的概念及结构
  • 堆的实现
    • 初始化
    • 销毁
    • 返回堆顶元素
    • 判空
    • 有效数据个数
  • 堆的插入(向上调整算法)
  • 删除堆顶元素,仍然保持堆的形态(向下调整算法)
  • 堆的创建
    • 向上调整法建堆
    • 向下调整建堆
    • 两种建堆方法时间复杂度
      • 向下调整法建堆时间复杂度分析
      • 向上调整法建堆时间复杂度分析
  • 堆排序

堆的概念及结构

如果有一个关键码的集合K = {k0 ,k1 ,k2 ,…,kn-1 },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:ki <=k2*i+1 且 <=k2*i+2 (ki >=k2*i+1 且 >=k2*i+2 ) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:
1.堆中某个节点的值总是不大于或不小于其父节点的值;
2.堆总是一棵完全二叉树。

在这里插入图片描述

根据数组下标我们可以得到父子结点下标之间的关系

在这里插入图片描述

堆的实现

堆其实就是一根完全二叉树,我们可以采用数组的方式实现

初始化

//初始化
void HeapInit(Heap* php)
{
	assert(php);
	php->a = NULL;
	php->size = php->capacity = 0;
}

销毁

//销毁
void HeapDestroy(Heap* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}

返回堆顶元素

//返回堆顶元素
HPDatatype HeapTop(Heap* php)
{
	assert(php);
	assert(!HeapEmpt(php));

	return php->a[0];
}

判空

//判空
bool HeapEmpt(Heap* php)
{
	assert(php);
	return php->size == 0;
}

有效数据个数

//数据个数
int HeapSize(Heap* php)
{
	assert(php);
	return php->size;
}

堆的插入(向上调整算法)

插入一个数据,也要保持大堆或(小堆)
在这里插入图片描述

void Swap(HPDatatype* x, HPDatatype* y)
{
	HPDatatype tmp = *x;
	*x = *y;
	*y = tmp;
}

//向上调整
void AdjustUp(HPDatatype* a, int child)
{
	int parent = (child - 1) / 2;
	//这里用孩子下标作为循环结束条件
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}

}

//插入,仍保持堆的形态
void HeapPush(Heap* php, HPDatatype x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDatatype* tmp = (HPDatatype*)realloc(php->a, newcapacity * sizeof(HPDatatype));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size] = x;
	php->size++;

	//向上调整
	AdjustUp(php->a, php->size - 1);
}

删除堆顶元素,仍然保持堆的形态(向下调整算法)

删除堆顶元素,是为了得到次大次少的元素。
既然用数组实现的堆,那删除元素可以直接像顺序表那样把后边元素向前移,覆盖前面元素吗?
在这里插入图片描述
显然是不可以的,这个就称不上堆了。
所以使用下面这种方法。
在这里插入图片描述

//向下调整
void AdjustDown(HPDatatype* a, int n, int parent)
{
	int minchild = parent * 2 + 1;
	while (minchild < n)
	{
		if (minchild + 1 < n && a[minchild+1] < a[minchild])
		{
			minchild++;
		}

		if (a[minchild] < a[parent])
		{
			Swap(&a[minchild], &a[parent]);
			parent = minchild;
			minchild = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}
}

//删除堆顶元素,仍然保持堆的形态
void HeapPop(Heap* php)
{
	assert(php);
	assert(!HeapEmpt(php));

	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	//向下调整
	AdjustDown(php->a, php->size, 0);
}

注意向下调整算法如果使用if和else来判断该父节的孩子结点谁大谁小这样会显得代码冗余,所以我们假设最小的是左孩子,然后左孩子和右孩子比较一下。看看那个是最小的,这样我们的代码会简单很多。值得我们借鉴。

其实大家发现没有,我们的建堆和堆排序已经完成了,

在这里插入图片描述

但是如果让我们排序一组数据,难道我们要把写一个堆然后在排序吗?

首先把数组的数据拷贝到堆里,然后进行建堆排序,结果我们只是让堆里面的数据变得有序了,数组里面的数据还是无序的,还得把堆里面数据拷贝回数组里,空间复杂度O(N);这样实在太麻烦了。所以我们考虑一下直接对数组进行建堆排序。

堆的创建

我们这里建的都是小堆。如果建大堆的话,改一下符号就可以了。

在这里插入图片描述
在这里插入图片描述

向上调整法建堆

void HeapSort(int* a, int n)
{
	//向上调整法建堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}
}

我们假设第一个元素就是一个堆,所以从下标为1的元素开始调整。

在这里插入图片描述

向下调整建堆

在这里插入图片描述

void HeapSort(int* a, int n)
{
	//向下调整法建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}
}

最后一个结点下标n-1,(n-1-1)/ 2是最后一个非叶子结点下标。

两种建堆方法时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的
就是近似值,多几个节点不影响最终结果):

向下调整法建堆时间复杂度分析

在这里插入图片描述

向上调整法建堆时间复杂度分析

在这里插入图片描述

因此建堆,我们应该选择向下调整建堆法

堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:

  1. 建堆
    升序:建大堆
    降序:建小堆

  2. 利用堆删除思想来进行排序
    建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

排序算法思想:每次交换第一个和最后一个元素,然后把最后一个元素不再看成堆里的元素,再进行向下调整(选择次大次小的元素位于堆顶)。依次下次直到所有元素排序完毕。

void HeapSort(int* a, int n)
{
	//向上调整法建堆O(NlogN)
	//for (int i = 1; i < n; i++)
	//{
	//	AdjustUp(a, i);
	//}


	//向下调整法建堆O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	//堆排序O(NlogN)
	int i = 1;
	while (i < n)
	{
		Swap(&a[0], &a[n - i]);
		AdjustDown(a, n - i, 0);
		++i;
	}
}

在这里插入图片描述

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

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

相关文章

shell脚本之数值,冒泡排序算法

目录 一、数组 1.定义数组 2. 数组中数据类型 2.1数值类型 2.2字符类型 二、数组的用法 1. 输出数组中的值 2. 统计数组参数个数 ​编辑 3.查看数组下标列表 4.分割字符串 5.替换数组中的字符 6.删除数组 三、数组追加元素 1.方法1示例 2.方法2示例 3.方法3示例 …

Notion Ai中文指令使用技巧

Notion AI 是一种智能技术&#xff0c;可以自动处理大量数据&#xff0c;并从中提取有用的信息。它能够 智能搜索&#xff1a;通过搜索文本和查询结果进行快速访问 自动归档&#xff1a;可以根据关键字和日期自动将内容归档 内容分类&#xff1a;可以根据内容的标签和内容的…

【嵌入式Linux】MBR分区表 和 GPT分区表

文章目录 GUID以及分区表MBR分区方案GPT 分区方案GPT分区表结构 GPT分区表LBALBA0LBA1LBA 2-33python生成GPT分区表gpt分区表实例 gpt分区表查看查看百问网T113-s3固件查看友善之臂nanopi-m1-plus官方固件查看荣品RV1126固件查看f1c200s固件查看V3s的SD启动卡 原文&#xff1a;…

session和Filter

session 介绍 jsp利用servlet提供的HttpSession接口识别一个用户&#xff0c;存储这个用户的所有访问信息。默认情况下&#xff0c;jsp允许会话跟踪&#xff0c;一个新的HttpSession对象将会自动地为新的客户端实例化。禁止会话跟踪需要关掉它&#xff0c;通过将page指令中ses…

多模态:BLIP-2论文讲解

多模态&#xff1a;BLIP-2论文讲解 IntroductionMethod第一阶段第二阶段 实验 Introduction 多模态学习在近两年我们已经见证了他的快速发展&#xff0c;由于它是视觉-语言的交叉领域&#xff0c;我们自然地期待可以借助目前风头正盛的LLM来辅助完成多模态任务。 在这篇论文中…

【SAP Abap】X-DOC:SNRO - ABAP流水号应用

【SAP Abap】X-DOC&#xff1a;SNRO - ABAP流水号应用 1、定义表&#xff08;字段域&#xff09;2、定义流水号3、使用流水号4、测试程序 1、定义表&#xff08;字段域&#xff09; 2、定义流水号 Tcode: SNRO/SNUM&#xff0c; 根据以上创建的字段域 YDSNRO&#xff0c;创建对…

grep练习题

题目: 文件格式&#xff1a; 第1列&#xff1a;城市位置编号。 第2列&#xff1a;月份。 第3列&#xff1a;存储代码及出库年份。 第4列&#xff1a;产品代号。 第5列&#xff1a;产品统一标价。 第6列&#xff1a;标识号。 第7列&#xff1a;合格数量。 file.txt文件内容&a…

RabbitMQ入门案例及简单工程搭建

环境的搭建 这里是用Maven工程搭建的基础环境项目&#xff0c;这里的dome_rabbitmq就是父工程。 子工程 publisher&#xff1a;消息发布者&#xff0c;将消息发送到队列queueconsumer&#xff1a;订阅队列&#xff0c;处理队列中的消息 父工程的pom文件依赖 <?xml ver…

SpringCloud-11_Alibaba Sentinel

SpringCloud系列 SpringCloud-10_Alibaba Nacos SpringCloud-9、SleuthZipkinSpringCloud-8、Gateway网关服务SpringCloud-7_OpenFeign服务调用SpringCloud-6_Ribbon负载均衡SpringCloud-5_模块集群化 求帮助 富文本编辑器用着真伤心TT。。不知道编辑器发生了什么。。--- 用…

Android Studio Notification(状态栏通知) 不显示通知 解决

引言&#xff1a;在学习的过程中&#xff0c;我发现&#xff0c;无论怎么修改&#xff0c;甚至是直接复制了大佬的代码&#xff0c;我的程序都不呢个正确的弹出一个状态栏通知&#xff0c;在经过一晚上的纠缠后&#xff0c;终于找到了原因 通知不显示可能由多种原因引起&#…

C++自动推导与函数模板

自动推导、函数模板、类模板 目录 1. 自动推导出数据类型 2. 函数模板 基本概念注意事项函数模板的具体化函数模板分文件编写函数模板高级函数后置返回类型 1. 自动推导出数据类型 auto关键字 linux 系统下使用的话&#xff0c;要在编译时 —stdc11 注意&#xff1a; a…

关于B+树的介绍、用途和c++代码实现

数据结构和算法的重要性不言而喻&#xff0c;一些优秀的开源项目的核心和灵魂就是数据结构、算法。在实际的编程中我们经常可以在各种框架、算法中看见B树、B树的身影。特别是在数据库的数据库引擎中&#xff0c;它们更是占据着重要的地位。 下面我将通过简单的二叉树&#xff…

2023/5/12总结

这俩天主要花时间在项目上&#xff1a; 实现了创建群聊和添加群聊&#xff1a; 点击创建群聊&#xff1a; &#xff0c;点击确定之后&#xff0c;会分配到一个群聊&#xff0c;默认头像会是下面这个圆形的头像&#xff1a; 添加群聊&#xff1a; 如果你要加入的群聊在自己的列…

06-HTML-列表标签

1、 <ul> 标签定义无序列表。 2、<ol> 标签定义有序列表。 属性值描述compactcompact HTML5 中不支持。HTML 4.01 中不赞成使用。 规定列表呈现的效果比正常情况更小巧。 reversedreversed规定列表顺序为降序。(9,8,7...)startnumber规定有序列表的起始值。type 1A…

为什么使用ConcurrentHashMap

currentHashMap的介绍 ConcurrentHashMap是线程安全并且高效的一种容器,我们就需要研究一下ConcurrentHashMap为什么既能够保证线程安全,又可以保证高效的操作。 为什么使用ConcurrentHashMap,我们就需要和HashMap以及HashTable进行比较&#xff1f; HashMap是线程不安全的&…

唐朔飞计组 第六章运算方法简单复习

在计算机中参与运算的数有两类&#xff1a;有符号数和无符号数 int 和unsigned unsigned可以看成是正数或者绝对值。 有符号数分为原码反码和补码 原码和反码的表示范围是相同的 但是补码由于将-0的位置换成2^n所以补码表示范围比原码和反码要多一位&#xff0c; 判断溢出比较…

诚邀社区开发者参与DeepBook测试和集成

DeepBook是Sui的基础流动性层&#xff0c;Sui基金会诚挚邀请社区开发者参与其测试和集成。 DeepBook为Sui的原生中央订单簿&#xff08;Central Limit Order Book&#xff0c;CLOB&#xff09;和基础流动性层&#xff0c;将会在未来数周准备完成&#xff0c;我们邀请大家参与测…

Unity大面积草地渲染——4、对大面积草地进行区域剔除和显示等级设置

目录 1、Shader控制一棵草的渲染 2、草地的动态交互 3、使用GPUInstancing渲染大面积的草 4、对大面积草地进行区域剔除和显示等级设置 Unity使用GPU Instancing制作大面积草地效果 大家好&#xff0c;我是阿赵。 这里开始讲大面积草地渲染的第四个部分&#xff0c;对大面积草地…

零知识证明:安全定义

之前在本科的课程仅仅略微介绍了下零知识证明&#xff0c;之后自学了一些相关内容&#xff0c;但不成体系。本学期跟着邓老师较为系统地学习了 ZKP&#xff0c;发现自己之前有很多的误解&#xff0c;临近期末整理下重要内容。 参考文献&#xff1a; Goldreich O. Foundations…

C语言实现个人通讯录(功能优化)

实战项目---通讯录&#xff08;功能优化&#xff09; 1.基本思路介绍&#xff1a;1.1基本思路&#xff1a; 2.通讯录的具体实现&#xff1a;2.1 通讯录的建立&#xff1a;2.2通讯录功能&#xff1a; 3.具体功能函数的实现&#xff1a;3.1 增添联系人&#xff1a;3.2 删除联系人…