【数据结构】(C语言):堆(二叉树的应用)

堆:

  • 此处堆为二叉树的应用,不是计算机中用于管理动态内存的堆。
  • 形状是完全二叉树。
  • 堆分两种:最大堆,最小堆。
  • 最大堆:每个节点比子树所有节点的数值都大,根节点为最大值。
  • 最小堆:每个节点比子树所有节点的数值都小,根节点为最小值。
  • 左右节点的数值没有顺序要求,但有左子节点才能有右子节点。
  • 一般用数组实现堆。最小堆可以实现优先队列。

注:完全二叉树:每个节点最多2个节点,倒数第二层级(含)之前层级的所有节点都有元素,最后一层级从左到右依次有元素(中间不能断)。

子树:节点及其所有子节点形成子树。(即节点,左右子节点,左右子节点的左右子节点,。。。)

根节点:没有父节点。

兄弟节点:其父节点是同一个节点。

父子节点在数组中的索引号
节点索引号(根节点为0)
节点x
父节点(x-1)// 2
左兄弟节点(右节点才有)x - 1
右兄弟节点(左节点才有)x + 1
左子节点2x + 1
右子节点2x + 2


C语言实现:(使用数组实现最小堆)

创建堆(结构体数据类型):记录数组地址和元素个数

// 堆(结构体数据类型)
typedef struct Heap
{
	int *p;			// 记录数组地址
	int n;			// 记录元素个数
} Heap;             // 别名

 (函数)堆初始化:

// 堆初始化
void init(Heap *heap, int length)
{
	heap->p = (int *)malloc(length * sizeof(int));    // 分配内存空间
	if(heap->p == NULL)
	{
		perror("Memory allocation failed");
		exit(-1);
	}
	heap->n = 0;            // 元素个数,初始化为0
}

 创建堆变量,并初始化

Heap heap;         // 创建堆变量
init(&heap, 8);    // 设置数组最大元素个数为8

添加元素:

1、先在末尾添加元素。

2、(向上调整)与父节点比较大小,若小于父节点,与父节点交换位置,直到开头位置(根节点,数组第一个元素)。

(注:子节点索引号为x,父节点索引号为(x-1)//2)

void add(Heap *heap, int data)		// add a element to the heap
{
	// 往末尾添加元素
	heap->p[heap->n] = data;
	// (向上调整) 与父节点比较,小于父节点的值,与父节点交换位置
	int cur = heap->n;
	while(cur > 0)
	{
		int parent = ceil((cur - 1) / 2);
		if(heap->p[parent] > data)
		{
			heap->p[cur] = heap->p[parent];
			heap->p[parent] = data;
			cur = parent;
		}
		else break;
	}
    // 每添加一个元素,元素个数+1
	heap->n++;
}

删除(根节点):

1、记录根节点(数组第一个元素)和末尾节点(数组最后一个元素)。

2、末尾节点的值换到根节点位置,元素个数-1。

3、(向下调整)从根节点开始,依次与左右子节点比较大小,数值小的是父节点,直到比对到末尾。

(注:父节点索引号为x,左子节点索引号为2x+1,右子节点索引号为2x+2)

int delete(Heap *heap)			// delete root from the heap
{
	if(heap->n == 0) return -1;    // 空树
	heap->n--;                     // 每删除一次根节点,元素个数-1
	int root = heap->p[0], enddata = heap->p[heap->n];
	heap->p[0] = enddata;	       // 末尾元素换到第一个位置
	// (向下调整) 与左右子节点比较,若子节点小,与较小子节点交换位置
	int cur = 0;
	while(cur <= heap->n)
	{
		int left = cur * 2 + 1, right = cur * 2 + 2, minchild;
		if(left > heap->n) break;				// 没有左子节点
		if(right > heap->n) minchild = left;	// 没有右子节点,有左子节点
		else									// 左右子节点都有的情况
		{
			// 左右子节点比较,找到较小子节点
			if(heap->p[right] < heap->p[left]) minchild = right;
			else minchild = left;
			// 父节点与较小子节点比较,若子节点小,与子节点交换位置
			if(heap->p[minchild] < enddata)
			{
				heap->p[cur] = heap->p[minchild];
				heap->p[minchild] = enddata;
				cur = minchild;
			}
			else break;
		}
	}
	return root;
}

获取根节点:(数组第一个元素)

int getroot(Heap *heap)			// get the root from the heap
{
	if(heap->n == 0)
	{
		printf("Empty heap\n");
		exit(-1);
	}
	return heap->p[0];
}

遍历堆:(类似广度遍历二叉树)

每个节点比子树的所有节点都小,但左右节点没有顺序要求。

void traverse(Heap *heap)		// show element one by one (like breadth traverse the tree)
{
	if(heap->n == 0) return ;

	printf("elements(%d): ", heap->n);
	for(int k = 0; k < heap->n; k++)
	{
		printf("%d  ", heap->p[k]);
	}
	printf("\n");
}

清空堆:

释放数组内存空间,指向数组的指针指向NULL,元素个数为0。

void clear(Heap *heap)		// clear the heap (free memory space of the array)
{
	free(heap->p);          // 释放数组内存空间
	heap->p = NULL;         // 指针指向NULL,避免野指针
	heap->n = 0;            // 元素个数为0
}

 


完整代码:(heap.c)

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

/* structure */
typedef struct Heap
{
	int *p;			// memory address of the heap (array)
	int n;			// the number of the heap
} Heap;

/* function prototype */
void init(Heap *, int);		// heap initialization
void add(Heap *, int);		// add a element to the heap
int delete(Heap *);			// delete root from the heap
int getroot(Heap *);		// get the root from the heap
void traverse(Heap *);		// show element one by one (likely breadth traverse the tree)
void clear(Heap *);			// clear the heap (free memory space of the array)

/* main function */
int main(void)
{
	Heap heap;
	init(&heap, 8);
	printf("length: %d\n", heap.n);

	add(&heap, 5);	
	printf("root(the minimum): %d\n", getroot(&heap));
	traverse(&heap);
	add(&heap, 8);	
	add(&heap, 3);	
	add(&heap, 9);	
	add(&heap, 2);
	printf("root(the minimum): %d\n", getroot(&heap));
	traverse(&heap);

	delete(&heap);	
	printf("root(the minimum): %d\n", getroot(&heap));
	traverse(&heap);
	delete(&heap);	
	printf("root(the minimum): %d\n", getroot(&heap));
	traverse(&heap);
	
	clear(&heap);
	printf("length: %d\n", heap.n);
	printf("root(the minimum): %d\n", getroot(&heap));
	return 0;
}

/* subfunction */
void init(Heap *heap, int length)		// heap initialization
{
	heap->p = (int *)malloc(length * sizeof(int));
	if(heap->p == NULL)
	{
		perror("Memory allocation failed");
		exit(-1);
	}
	heap->n = 0;
}

void add(Heap *heap, int data)		// add a element to the heap
{
	// add a element to the end of the heap
	heap->p[heap->n] = data;
	// (adjust up) compair with parent,if smaller than parent, change the position
	int cur = heap->n;
	while(cur > 0)
	{
		int parent = ceil((cur - 1) / 2);
		if(heap->p[parent] > data)
		{
			heap->p[cur] = heap->p[parent];
			heap->p[parent] = data;
			cur = parent;
		}
		else break;
	}
	heap->n++;
}

int delete(Heap *heap)			// delete root from the heap
{
	if(heap->n == 0) return -1;
	heap->n--;
	int root = heap->p[0], enddata = heap->p[heap->n];
	heap->p[0] = enddata;	// put the last element to the first position
	// (adjust down) compair with left and right child, minimun is parent
	int cur = 0;
	while(cur <= heap->n)
	{
		int left = cur * 2 + 1, right = cur * 2 + 2, minchild;
		if(left > heap->n) break;				// no left child
		if(right > heap->n) minchild = left;	// have left child, no right child
		else									// have left child and right child
		{
			// compair left child and right child, find smaller one
			if(heap->p[right] < heap->p[left]) minchild = right;
			else minchild = left;
			// smaller child compair with parent,if child is the smallest, change
			if(heap->p[minchild] < enddata)
			{
				heap->p[cur] = heap->p[minchild];
				heap->p[minchild] = enddata;
				cur = minchild;
			}
			else break;
		}
	}
	return root;
}

int getroot(Heap *heap)			// get the root from the heap
{
	if(heap->n == 0)
	{
		printf("Empty heap\n");
		exit(-1);
	}
	return heap->p[0];
}

void traverse(Heap *heap)		// show element one by one (like breadth traverse the tree)
{
	if(heap->n == 0) return ;

	printf("elements(%d): ", heap->n);
	for(int k = 0; k < heap->n; k++)
	{
		printf("%d  ", heap->p[k]);
	}
	printf("\n");
}

void clear(Heap *heap)		// clear the heap (free memory space of the array)
{
	free(heap->p);
	heap->p = NULL;
	heap->n = 0;
}

编译链接: gcc -o heap heap.c

执行可执行文件: ./heap

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

千万不要用国产BI,不然你会发现它性价比奇高——以奥威BI软件为例

在信息技术日新月异的今天&#xff0c;企业对于商业智能&#xff08;BI&#xff09;软件的选择往往陷入了一个误区&#xff1a;盲目追求国际品牌&#xff0c;却忽视了身边那些性价比极高的国产精品。如果你不慎踏入了“千万不要用国产BI”的陷阱&#xff0c;那么奥威BI软件将是…

PHP家政服务预约单开版微信小程序系统源码

&#x1f3e0; —— 便捷生活&#xff0c;从指尖开始&#x1f4aa; &#x1f308;【开篇&#xff1a;家政新风尚&#xff0c;一键触达】 在忙碌的生活节奏中&#xff0c;你是否渴望拥有一个温馨、整洁的家&#xff0c;却又苦于找不到合适的家政服务&#xff1f;现在&#xff…

C++_03

1、构造函数 1.1 什么是构造函数 类的构造函数是类的一种特殊的成员函数&#xff0c;它会在每次创建类的新对象时执行。 每次构造的是构造成员变量的初始化值&#xff0c;内存空间等。 构造函数的名称与类的名称是完全相同的&#xff0c;并且不会返回任何类型&#xff0c;也不…

对标GPT-4o!不锁区、支持手机、免费使用,Moshi来啦!

7月4日凌晨&#xff0c;法国知名开源AI研究实验室Kyutai在官网发布了&#xff0c;具备看、听、说多模态大模型——Moshi。 Moshi功能与OpenAI在5月14日展示的最新模型GPT-4o差不多&#xff0c;可以听取人的语音提问后进行实时推理回答内容。但GPT-4o的语音模式要在秋天才能全面…

适合弱电行业的项目管理软件!找企智汇软件!

随着科技的不断发展&#xff0c;弱电行业对于项目管理的需求日益增强。为满足这一需求&#xff0c;企智汇推出了一款专为弱电行业打造的工程项目管理系统。 企智汇弱电行业工程项目管理系统以其专业性、高效性和智能性&#xff0c;赢得了业界的广泛认可。该系统深入融合了弱电…

仓库管理系统

create database Warehouse_management;//建库 use Warehouse_management; 一、建表 1、管理员信息表 CREATE TABLE ManagerInfo (Mno CHAR(3) PRIMARY KEY,Mname VARCHAR(10) NOT NULL,Mgender CHAR(1) DEFAULT 男,Mbirhdate DATE,Mtelephone CHAR(11) NOT NULL,Mhiredate …

数据预处理:统计关联性分析/数据清洗/数据增强/特征工程实例

专栏介绍 1.专栏面向零基础或基础较差的机器学习入门的读者朋友,旨在利用实际代码案例和通俗化文字说明,使读者朋友快速上手机器学习及其相关知识体系。 2.专栏内容上包括数据采集、数据读写、数据预处理、分类\回归\聚类算法、可视化等技术。 3.需要强调的是,专栏仅介绍主…

C++(第四天----拷贝函数、类的组合、类的继承)

一、拷贝构造函数&#xff08;复制构造函数&#xff09; 1、概念 拷贝构造函数&#xff0c;它只有一个参数&#xff0c;参数类型是本类的引用。如果类的设计者不写拷贝构造函数&#xff0c;编译器就会自动生成拷贝构造函数。大多数情况下&#xff0c;其作用是实现从源对象到目…

Access,Trunk,Hybrid网络设备链接类型详解

带着问题找答案&#xff1a;网络链路上的数据包怎么看&#xff0c;是否携带vlan-id如何看&#xff0c;以及如何设计链接类型满足用户要求&#xff0c;请看如下解析。 第一种&#xff1a;链接类型access 无标记数据帧 第二种&#xff1a;链接类型trunk 第三种&#xf…

最新mysql打开远程访问和修改最大连接数

这里写目录标题 1.使用navicat进入命令控制板,进入use mysql;2.查询用户表3.更新user表中root用户域属性&#xff0c;%表示允许外部访问4.执行以上语句之后再执行&#xff0c;FLUSH PRIVILEGES;5. 执行授权语句修改最大连接数 1.使用navicat进入命令控制板,进入use mysql; use…

为什么写Python脚本时要加上if __name__ == ‘__main__‘?

目录 一、__name__ 的秘密 二、if __name__ __main__: 的作用 三、代码示例与案例分析 示例一&#xff1a;简单的数学工具模块 示例二&#xff1a;命令行工具 四、实际应用场景 五、进阶应用 1. 插件开发 2. 动态加载模块 3. 交互式与脚本模式切换 六、结论 在Pyth…

电商API对接流程丨从零开始快速打通电商平台数据通道

开发电商业务管理系统时&#xff0c;怎么对接电商接口呢&#xff1f;有两种方式可供选择&#xff0c;一种方式就是自己入驻想要对接的电商平台对应的开放平台&#xff0c;按照要求与流程与电商接口进行对接&#xff0c;还有一种方式就是寻找电商中台&#xff0c;通过第三方接口…

吴恩达深度学习笔记:机器学习策略(2)(ML Strategy (2)) 2.5-2.6

目录 第三门课 结构化机器学习项目&#xff08;Structuring Machine Learning Projects&#xff09;第二周&#xff1a;机器学习策略&#xff08;2&#xff09;(ML Strategy (2))2.5 数据分布不匹配时的偏差与方差的分析&#xff08;Bias and Variance with mismatched data di…

玩机进阶教程----MTK芯片杂牌机 小品牌机型解除bl锁以及root的操作步骤解析

在玩机过程中会遇到很多小品牌机型或者杂牌机类的。大多都使用mtk芯片。而且基本很少有官方线刷包。在这些机型中玩机首先我们要想办法导出系统来制作线刷包。以免后续解锁bl或者root出现未知故障可以恢复原系统。 那么对于这些机型该如何进行备份固件和root呢。通过博文可以初…

选微调、RAG还是微调+RAG?

RAG技术是一种结合了检索与生成的方法。它通常依赖于两个核心组件&#xff1a;一个大型语言模型&#xff08;如GPT-3&#xff09;和一个检索系统&#xff08;如向量数据库&#xff09;。RAG先使用检索系统从大量数据中检索出相关信息&#xff0c;然后将这些信息提供给语言模型&…

一文带你看懂什么是营销归因模型及SaaS企业的应用

在数字化时代&#xff0c;营销活动的多样性和复杂性使得评估其效果成为一项挑战。营销归因模型应运而生&#xff0c;为SaaS企业等提供了科学、系统的评估工具。本文将简要介绍什么是营销归因模型&#xff0c;阐述其带来的好处&#xff0c;并探讨SaaS企业可以采用的营销归因系统…

编译rust程序,并让它依赖低版本的GLIBC库

在linux环境下编译rust程序&#xff0c;编译好的程序会依赖你当前系统的GLIBC库&#xff0c;也就是说你的程序无法在使用更低版本GLIBC库的linux系统中运行。 查看当前系统的GLIBC版本&#xff1a; strings /lib64/libc.so.6 | grep GLIBC 为了让编译的程序依赖比较低版本的GL…

通过 Power Automate 以提升的权限运行 Power Apps 连接

使用Power Apps在Sharepoint列表中新建或编辑项比较简单&#xff0c;就是创建窗体&#xff0c;连接Sharepoint列表&#xff0c;添加个按钮&#xff0c;触发条件为Submit(form)。 填写信息&#xff0c;点击按钮即可新建项 但使用过程中&#xff0c;发现运行此应用的用户&#xf…

mac 11 变编译安装nginx

mac 11 变编译安装nginx 记录一次安装过程 所需要的包 pcre: https://sourceforge.net/projects/pcre/files/pcre/OpenSSL: https://www.openssl.org/source/Nginx: https://nginx.org/en/download.html如果没有pcre 和Openssl,报错如下 把pcre和Openssl 解压到nginx 目录下…

MySQL数据库的备份-恢复-日志

一、备份 1.1数据备份的重要性 备份的主要目的是灾难恢复。 在生产环境中&#xff0c;数据的安全性至关重要。 任何数据的丢失都可能产生严重的后果。 1.2造成数据丢失的原因 程序错误人为操作错误运算错误磁盘故障灾难&#xff08;如火灾、地震&#xff09;和盗窃 1.3数…