【数据结构初阶】二叉树--堆(顺序结构实现)

news2024/9/20 0:58:27

hello!

目录

一、实现顺序结构二叉树

1.1  堆的概念和结构

1.2  堆及二叉树的性质

1.3  堆的实现

1.3.1  创建堆的结构

1.3.2  初始化和销毁

1.3.3  入堆+向上调整算法(创建一个小堆)

1.3.4  出堆+向下调整算法(小堆)

1.3.5  判空+取堆顶数据+堆中有效数据个数

二、顺序结构二叉树---源码

Heap.h

Heap.c

test.c

Relaxing Time!

—————————————  《星空物语》  —————————————


正文开始——

一、实现顺序结构二叉树

一般使用顺序结构的数组来存储数据,堆是一种特殊的二叉树,分为大根堆(大堆)和小根堆(小堆),具有二叉树的特性的同时,还具备其他的特性。

1.1  堆的概念和结构

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

小堆:父结点不大于孩子结点;大堆:父结点不小于孩子结点。

数组不一定是有序地。小堆堆顶是堆的最小值,大堆堆顶是堆的最大值。 

1.2  堆及二叉树的性质

堆的性质

  • 堆中某个结点的值总是不大于或不小于其父结点的值;
  • 堆总是一棵完全二叉树。

二叉树的性质

  • 对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0开始编号,则对于序号为 i 的结点有:
  1.  若 i > 0,i 位置结点的双亲序号:( i - 1)/ 2;i = 0,i 为根结点编号,无双亲结点;
  2. 若 2i + 1 < n,左孩子序号:2i + 1,2i + 1 >= n 则无左孩子;
  3. 若 2i + 2 < n,右孩子序号:2i + 2,2i + 2 >= n 则无右孩子;

通俗点来讲,父结点i---> 左孩子:2i+1,右孩子:2i+2。

1.3  堆的实现

1.3.1  创建堆的结构

堆的底层结构是数组 

//创建堆的结构
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* arr;
	int size;//堆中有效数据的个数
	int capacity;//堆的容量
}HP;

1.3.2  初始化和销毁

//初始化
void HPInit(HP* php)
{
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}

//销毁
void HPDestroy(HP* php)
{
	assert(php);

	 if(php->arr)
	{
		free(php->arr);
		php->arr = NULL;
	}

	php->size = php->capacity = 0;

}

1.3.3  入堆+向上调整算法(创建一个小堆)

将新数据插入到数组的尾上,再进行向上调整算法,直到满足堆。

向上调整算法

  • 先将元素插入到堆的末尾,即最后一个子结点之后;
  • 插入之后如果堆的性质遭到破坏,将新插入结点顺着双亲结点往上调整到合适位置即可。 

【举例,向上调整算法】

思路:新插入的数据作为子结点(child),找到新插入数据的父结点(parent=(child-1)/ 2)(上面二叉树的性质),父结点和子结点进行比较,若父结点大于子结点,数据交换,不大于则不交换。再找新的父结点和子结点,循环条件是 child>0,child不需要等于0,child等于0时为根结点,根结点没有父结点不需要发生交换。

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

//向上调整算法
void AdjustUp(HPDataType* arr, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//入堆
void HPPush(HP* php, HPDataType x)
{
	assert(php);

	//判断空间是否充足
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->arr, newCapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc file!");
			exit(1);
		}
		php->arr = tmp;
		php->capacity = newCapacity;
	}
	//此时空间已经充足
    //我们应该清楚地知道,size是x的下标,size在数组中指向x这个元素
	php->arr[php->size] = x;

	//向上调整算法
	AdjustUp(php->arr, php->size);

	php->size++;
}

1.3.4  出堆+向下调整算法(小堆)

堆的删除(出堆)

删除堆是删除堆顶的数据,将堆顶的数据跟最后一个数据进行交换,然后删除数组最后一个数据,再进行向下调整算法。

向下调整算法有一个前提:左右子树必须是一个堆,才能进行调整。

出堆

  • 将堆顶元素与堆中最后一个元素进行交换;
  • 删除堆中最后一个元素;
  • 将堆顶元素向下调整到满足堆特性为止。

 【向下调整算法】

思路:堆顶元素为父结点,找到左右孩子中最小的那个子结点与之比较,若父结点大于子结点,交换,不大于则不交换,不断找新的父结点和子结点,就这样循环,注意循环结束的条件。上代码,结合代码中的注释更好的理解。

//向下调整算法
void AdjustDown(HPDataType* arr, int parent, int n)
{
	int child = parent * 2 + 1;//左孩子
	while (child < n)
	{
		//找左右孩子中最小的
        //child + 1 < n , 保证不越界
		if (child + 1 < n && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[parent] > arr[child])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//出堆
void HPPop(HP* php)
{
	assert(php && php->size);
    
	Swap(&php->arr[0], &php->arr[php->size-1]);
	php->size--;//删除掉最后一个数据(堆顶元素)

	//向下调整算法
	AdjustDown(php->arr, 0, php->size);

}

1.3.5  判空+取堆顶数据+堆中有效数据个数

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

//取堆顶数据
HPDataType HPTop(HP* php)
{
	assert(php && php->size);

	return php->arr[0];
}

//堆中有效数据的个数
int HPSize(HP* php)
{
	assert(php);
	return php->size;
}

二、顺序结构二叉树---源码

Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

//创建堆的结构
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* arr;
	int size;//堆中有效数据个数
	int capacity;//堆的容量
}HP;

//初始化
void HPInit(HP* php);

//销毁
void HPDestroy(HP* php);

//入堆
void HPPush(HP* php, HPDataType x);

//出堆
void HPPop(HP* php);

//判空
bool HPEmpty(HP* php);

//取堆顶数据
HPDataType HPTop(HP* php);

//堆中有效数据的个数
int HPSize(HP* php);

Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"

//初始化
void HPInit(HP* php)
{
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}

//销毁
void HPDestroy(HP* php)
{
	assert(php);
	if (php->arr)
	{
		free(php->arr);
		php->arr = NULL;
	}
}

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

//向上调整算法
void AdjustUp(HPDataType* arr, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)//不需要等于0,child等于0时为根结点,根结点没有父结点不需要发生交换
	{
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//入堆
void HPPush(HP* php,HPDataType x)
{
	assert(php);

	//判断空间是否充足
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->arr, newCapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc file!");
			exit(1);
		}
		php->arr = tmp;
		php->capacity = newCapacity;
	}

	//此时空间已经充足
	php->arr[php->size] = x;

	//向上调整算法
	AdjustUp(php->arr, php->size);

	php->size++;

}

//向下调整算法
void AdjustDown(HPDataType* arr, int parent, int n)
{
	int child = parent * 2 + 1;//左孩子
	while (child < n)
	{
		//找左右孩子中最小的
		if (child + 1 < n && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[parent] > arr[child])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//出堆
void HPPop(HP* php)
{
	assert(php && php->size);

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

	//向下调整算法
	AdjustDown(php->arr, 0, php->size);

}

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

//取堆顶数据
HPDataType HPTop(HP* php)
{
	assert(php && php->size);

	return php->arr[0];
}

//堆中有效数据的个数
int HPSize(HP* php)
{
	assert(php);
	return php->size;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"

void test01()
{
	HP hp;
	HPInit(&hp);

	int arr[] = { 1,3,5,7,4,10,8 };

	for (int i = 0; i < 7; i++)
	{
		HPPush(&hp, arr[i]);
	}

	printf("堆中有效数据个数:%d\n", HPSize(&hp));

	while (!HPEmpty(&hp))
	{
		printf("%d ", HPTop(&hp));
		HPPop(&hp);
	}



	HPDestroy(&hp);


}

int main()
{

	test01();
	return 0;
}

完——


Relaxing Time!

—————————————  《星空物语》  —————————————

星空物语(电视剧《一起来看流星雨》主题曲) - 张翰/朱梓骁/魏晨/俞灏明 - 单曲 - 网易云音乐

我是云边有个稻草人

期待与你的下一次相遇——

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

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

相关文章

linux组合命令:删除一个目录树中所有的空目录(包括嵌套空目录)

目录 一、需求 二、实现方式 1、结合使用 find 命令删除空目录 &#xff08;1&#xff09;删除目录的方式 &#xff08;2&#xff09;只删除空目录 2、更高效的方法 &#xff08;1&#xff09;使用 find 搭配 -delete &#xff08;2&#xff09;实际效果 三、相关命令…

STM32MP157_uboot_命令使用

STM32MP157_uboot_命令使用 前言&#xff1a; 进入 uboot 的命令行模式以后输入“help”或者“&#xff1f;”&#xff0c;然后按下回车即可查看当前 uboot 所支持的命令&#xff0c;图 中只是 uboot 的一部分命令&#xff0c;具体的命令列表以实际为准。图中的命令并不是 uboo…

SpringBoot2:IOC容器的相关操作以及常用注解说明

一、查看容器中的Bean实例 查看springboot中的容器实例&#xff0c;首先&#xff0c;我们要获取到IOC容器。 //1、返回我们的IOC容器ConfigurableApplicationContext run SpringApplication.run(MainApplication.class, args);//2、查看容器里面的组件String[] names run.ge…

线性代数基础(2)——特征值和特征向量

第一节博客已经整理了求导的公式&#xff0c;一些常用的概念。链接如下&#xff1a;高等数学基础&#xff08;1&#xff09;-CSDN博客。 第二节博客整理了微积分的公式及其相关概念。链接如下&#xff1a;高等数学基础&#xff08;2&#xff09;——微积分-CSDN博客 第三节博客…

JavaWeb JavaScript ⑩ 日程管理 第一期

自我消耗&#xff0c;敏感是我&#xff0c; 明媚是我&#xff0c; 我横跳在不同的情绪中 —— 24.8.31 一、登录页及校验 1.校验账号格式 // 校验账号格式function checkUsername(){// 定义正则表达式表示字符串规则var usernameReg /^[a-zA-Z0-9]{5,10}$/;// 获取用户名输入…

96.WEB渗透测试-信息收集-Google语法(10)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;95.WEB渗透测试-信息收集-Google语法&#xff08;9&#xff09; • site &#xff1a; x…

【硬件操作入门】3--同步与异步、半双工传输、UART硬件介绍、bps速率计算

【硬件操作入门】3–同步与异步、半双工传输、UART硬件介绍、bps速率计算 文章目录 【硬件操作入门】3--同步与异步、半双工传输、UART硬件介绍、bps速率计算一、同步与异步1.1. 概念1.2 同步信号1.3 异步信号1.4 举个例子&#xff1a;红外遥控器解码器&#xff08;异步&#x…

一分钟学会系列-1电烙铁(焊台)

目录&#xff1a; ‌1、概述 2、电烙铁的种类 3、焊接步骤 4、电烙铁的保养 ‌1、概述 电烙铁‌是一种电子制作和电器维修中常用的工具&#xff0c;主要用于焊接元件及导线。使用电烙铁时&#xff0c;需要注意安全&#xff0c;并采取适当的预防措施&#xff0c;如使用风扇…

最新ssl证书在线申请源码+网站ICP备案查询源码

最新ssl证书在线申请源码网站ICP备案查询源码 经过精心调试和修复&#xff0c;我们对源码进行了优化&#xff0c;确保了证书价格的准确显示。现在&#xff0c;您可以放心使用我们的在线生成网站源码&#xff0c;完整地展示证书价格&#xff0c;并在生成证书时提供准确的价格计…

SQL-函数

1、字符串函数 # 字符函数 select concat(hello , mysql!); select lower(HELLO); select upper(hello); select lpad(01,5,-);# 左填充 select rpad(01,5,-);# 右填充 select trim( hello mysql ! );# 去除前后空格 select substring(hello mysql!,1,7);# 截取一部分字符前7…

【教学类-35-21】20240901 中2班描字帖(学号+姓名、虚拟姓名、杨任东竹石体 Regular)

背景需求&#xff1a; 8月底通知我成为中2班的班主任&#xff0c;为了快速识别幼儿的脸、姓名、学号&#xff0c;再次制作描字帖&#xff0c;并拍照。 最近做的一份字帖是中4班描字帖 【教学类-35-20】20240328 中4班描字帖&#xff08;学号姓名 A4竖版2份 横面&#xff09;…

--- 数据结构 链表 --- java

与顺序表相比&#xff0c;链表的最大优点就是不会存在空间的浪费 链表是通过将一个一个储存数据的节点&#xff08;对象&#xff09;连起来&#xff08;通过记录下他们的地址&#xff09;&#xff0c;这些数据在逻辑上就是线性的&#xff0c;但在物理上不是&#xff0c;因为地…

[Leetcode 216][Medium]组合总和 III--回溯(组合问题)

目录 一、题目描述 二、整体思路 三、代码 一、题目描述 原题地址 二、整体思路 对于组合问题&#xff0c;首先要想到回溯法。那么可以根据回溯法模版进行设计。 void backtrace(元素){if(满足题目要求的条件){保存目前路径/状态/结果;return;}for循环,往目前状态相邻的所…

9-8 束搜索

贪心搜索 穷举搜索 束搜索 小结 序列搜索策略包括贪心搜索、穷举搜索和束搜索。 贪心搜索所选取序列的计算量最小&#xff0c;但精度相对较低。 穷举搜索所选取序列的精度最高&#xff0c;但计算量最大。 束搜索通过灵活选择束宽&#xff0c;在正确率和计算代价之间进行权衡…

栈和队列——用队列实现栈

题目中给出&#xff0c;让我们应用两个队列实现栈&#xff0c;首先我们先来想一下&#xff0c;栈是先进后出&#xff0c;队列是先进先出。所以我们就需要应用两个队列来回导才能实现栈的特点。因为这道题是基于队列来实现的&#xff0c;所以在下方若有看不懂的函数名称可以去栈…

Java 入门指南:Java 并发编程 —— Synchronized 实现悲观锁(Pessimistic Locking)

悲观锁 悲观锁&#xff08;Pessimistic Locking&#xff09;是一种悲观的并发控制机制&#xff0c;它基于悲观的假设&#xff0c;即并发冲突会时常发生&#xff0c;因此在访问共享资源&#xff08;如数据库记录或共享变量&#xff09;之前&#xff0c;会先获取独占性的锁&…

数组结构第一周做题总结_基础练习

错误的原因是写了无参构造函数但是没有实现 id:8 A. 月份查询&#xff08;指针数组&#xff09; 题目描述 已知每个月份的英文单词如下&#xff0c;要求创建一个指针数组&#xff0c;数组中的每个指针指向一个月份的英文字符串&#xff0c;要求根据输入的月份数字输出相应的…

盘点java8 stream中隐藏的函数式接口

shigen坚持更新文章的博客写手&#xff0c;记录成长&#xff0c;分享认知&#xff0c;留住感动。个人IP&#xff1a;shigen 提到函数式接口&#xff0c;最常见的就是lambda表达式&#xff0c;IDEA也有智能的提示&#xff1a; 最后改成这样的就是最简洁的、IDEA希望的风格&#…

机器学习:基于机器学习的中文评论情感分析

通过机器学习技术对中文评论进行情感分析。我们使用了jieba进行中文分词&#xff0c;移除了停用词&#xff0c;并利用词袋模型&#xff08;Bag of Words&#xff09;和多项式朴素贝叶斯分类器对评论进行了情感分类。实验结果表明&#xff0c;该模型在测试集上达到了较高的准确率…

如何构建高效的中药材进存销管理系统?——运用Java SpringBoot和Vue实现库存实时监控,简化销售流程,提升药材管理效率。

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…