【数据结构】堆(超详细)

文章目录

  • 前言
  • 堆的概念及结构
  • 堆的实现
    • 堆的向下调整算法(建小堆为例)
    • 堆的向上调整算法(建小堆为例)
    • 堆的初始化
    • 销毁堆
    • 堆的插入
    • 堆的删除(规定删堆顶的数据)
    • 取堆顶元素
    • 判断堆是否为空
    • 获取堆的个数
  • 完整代码(包括测试代码)
    • Heap.h
    • Heap.c
    • test.c

前言

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。在现实中我们通常把堆 (一种完全二叉树) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

堆的概念及结构

在这里插入图片描述

【大根堆和小根堆】:

根结点最大的堆叫做大根堆,树中所有父亲都大于或等于孩子。

根结点最小的堆叫做小根堆,树中所有父亲都小于或等于孩子。
在这里插入图片描述

堆的实现

首先新建一个工程:

Heap.h(堆的类型定义、接口函数声明、引用的头文件)
Heap.c(堆接口函数的实现)
test.c(主函数、测试栈各个接口功能)

完整的代码放在后面(包括测试代码),这里就不会展示测试的效果图。大家可以自己别敲边按测试代码测试。图解会写的很详细的,么么😙

堆的向下调整算法(建小堆为例)

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。
向下调整算法有一个前提:左右子树必须是一个堆,才能调整
int arr[] = {27,15,19,18,28,34,65,49,25,37};
在这里插入图片描述

思想:
1.选出左右孩子较小的一个值,
2.然后和父亲进行比较,如果比父亲小就进行交换,并将原来小的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。如果比父亲大,则停止向下调整,此时该树就成小堆。

//交换函数
void Swap(HDataType* p1, HDataType* p2)
{
	HDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;

}

//向下调整算法
void AdjustDown(HDataType* a, int size, int parent)
{
	//假设左孩子小
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] < a[child])//确保有右孩子,如果右孩子小,更新到右边
		{
			++child;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
            //更新父亲孩子下标,
			parent = child;
			child = child * 2 + 1;
		}
		else // 如果父亲小于孩子,说明已经为小堆了,停止调整
		{
			break;
		}
	}

}

堆的向上调整算法(建小堆为例)

当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。
假设在这个堆int arr[] = {15,18,19,25,28,34,65,49,27,37}的末尾插入16.
在这里插入图片描述

思想:
1.将要插入孩子的值与父亲的值比较。
2.若插入孩子的值比父亲的值小,则交换插入孩子的值与父亲的值,并将父亲的位置当作新的插入孩子的值继续进行向上调整。若插入孩子的值比父亲的值大,则停止向上调整,此时该树就成小堆。


//交换函数
void Swap(HDataType* p1, HDataType* p2)
{
	HDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;

}

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

堆的初始化

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

销毁堆

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

堆的插入

数据插入时是插入到数组的末尾,即树形结构的最后一层的最后一个结点,所以插入数据后我们需要运用堆的向上调整算法对堆进行调整,使其在插入数据后仍然保持堆的结构。

//堆的插入
void HPPush(HP* php, HDataType x)
{
	assert(php);
	//检查空间是否足够插入,不够则扩容
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HDataType* tmp = (HDataType*)realloc(php->a, sizeof(HDataType) * newcapacity);
		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);// 从插入的元素开始,进行向上调整,保持它依然是堆
}

堆的删除(规定删堆顶的数据)

堆的删除,删除的是堆顶的元素,但是这个删除过程可并不是直接删除堆顶的数据,
1.将堆顶的数据与最后一个结点的数据交换,
2.删除堆种最后一个元素,此时左子树和右子树还是小堆
3.再对堆进行向下调整

//堆的删除(规定删堆顶的数据)
void HPPop(HP* php)
{
	assert(php);
	assert(php->size > 0);//确保堆不能为空
	Swap(&php->a[0], &php->a[php->size - 1]);// 将堆顶元素和最后一个元素交换
	php->size--;// 删除堆中最后一个元素
	AdjustDown(php->a, php->size, 0);// 从根节点开始,对剩下元素进行向下调整成大堆,保持它依然是堆
}

取堆顶元素

//取堆顶元素
HDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);//确保堆里有数据
	return php->a[0];//返回堆顶数据
}

判断堆是否为空

//判断堆是否为空
bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;//判断堆中数据是否为0
}

获取堆的个数

//获取堆的个数
int HPSize(HP* php)
{
	assert(php);
	return php->size;//返回堆中数据个数
}

完整代码(包括测试代码)

Heap.h

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


typedef  int HDataType;
typedef struct Heap
{
	HDataType* a;
	int size;
	int capacity;

}HP;

//交换函数
void Swap(HDataType* p1, HDataType* p2);
//向上调整算法
void AdjustUp(HDataType* a, int child);
//向下调整算法
void AdjustDown(HDataType* a, int size, int parent);



//初始化堆
void HPInit(HP* php);
//销毁堆
void PHDestroy(HP* php);
//堆的插入
void HPPush(HP* php, HDataType x);
//堆的删除(规定删堆顶的数据)
void HPPop(HP* php);
//取堆顶元素
HDataType HPTop(HP* php);
//判断堆是否为空
bool HPEmpty(HP* php);
//获取堆的个数
int HPSize(HP* php);



Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"


//小堆
void Swap(HDataType* p1, HDataType* p2)
{
	HDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;

}

//向下调整算法
void AdjustDown(HDataType* a, int size, int parent)
{
	//假设左孩子小
	int child = parent * 2 + 1;

	while (child < size)
	{	
		if (child + 1 < size && a[child + 1] < a[child])//确保有右孩子,如果错了,更新到右边
		{
			++child;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);

			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}

}
//向上调整算法
void AdjustUp(HDataType* a, int child)//可以用递归写,没必要,用循环就可以
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}


	}

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

//销毁堆
void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;

}

//堆的插入
void HPPush(HP* php, HDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HDataType* tmp = (HDataType*)realloc(php->a, sizeof(HDataType) * newcapacity);
		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 HPPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	AdjustDown(php->a, php->size, 0);
}


//取堆顶元素
HDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

//判断堆是否为空
bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}
//获取堆的个数
int HPSize(HP* php)
{
	assert(php);
	return php->size;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"


int main()
{
	int a[] = { 4,3,7,9,1,5,8,2,8 };
	int sz = sizeof(a) / sizeof(a[0]);
	HP hp;
	HPInit(&hp);
	for (int i = 0; i < sz; i++)
	{
		HPPush(&hp, a[i]);
	}
	//int k = 5;
	//while (k--)
	//{
	//	printf("%d\n", HPTop(&hp));
	//	HPPop(&hp);
	//}
	while (!HPEmpty(&hp))
	{
		printf("%d ", HPTop(&hp));
		HPPop(&hp);
	}
	printf("\n");

	return 0;
}

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

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

相关文章

k8s 二进制安装 详细安装步骤

目录 一 实验环境 二 操作系统初始化配置&#xff08;所有机器&#xff09; 1&#xff0c;关闭防火墙 2&#xff0c;关闭selinux 3&#xff0c;关闭swap 4, 根据规划设置主机名 5, 做域名映射 6&#xff0c;调整内核参数 7&#xff0c; 时间同步 三 部署 dock…

微软exchange邮箱发送

使用java发送exchange类型的邮件&#xff0c;foxmail中配置如下图&#xff1a; 需要的maven依赖如下&#xff1a; <dependency><groupId>com.microsoft.ews-java-api</groupId><artifactId>ews-java-api</artifactId><version>2.0</ve…

1. 杜克大学官方宣布2027届新生画像什么是vue关键特点核心概念简单示例生态系统

目录 1. 杜克大学官方宣布2027届新生画像 什么是vue 关键特点 核心概念 简单示例 生态系统 1. 杜克大学官方宣布2027届新生画像 杜克大学校报《The Chronicle》已连续第七年对杜克大学的一年级新生进行深入调查&#xff0c;探讨该群体家庭受教育背景、家庭收入水平以及…

异步I/O库-libuv介绍

1.简介 libuv是一个跨平台的支持事件驱动的异步I/O的库&#xff0c;使开发者可以以非阻塞的方式执行文件I/O操作、网络通信、子进程管理等。 libuv的主要特点包括&#xff1a; 事件循环&#xff1a;libuv有一个基于事件循环的模型&#xff0c;它不断地轮询事件&#xff0c;并…

洗地机怎么挑?洗地机选购指南,2024洗地机测评选购攻略

在快节奏的生活中&#xff0c;繁琐的清洁工作往往令人头疼&#xff0c;随着洗地机的诞生&#xff0c;极大地简化了清洁的过程&#xff0c;洗地机凭借着它吸拖洗为一体的高效清洁特点&#xff0c;受到家庭和商业场所的广泛欢迎。那么&#xff0c;洗地机怎么挑&#xff0c;要注意…

速度背!24上软考网工“经典100道母题来了”!

距离软考考试的时间越来越近了&#xff0c;趁着这两周赶紧准备起来。 今天给大家整理了——网络工程师经典100道母题&#xff08;含解析&#xff09;&#xff0c;有PDF版&#xff0c;可打印&#xff0c;每天刷一点&#xff0c;考试就像遇到“老朋友”。 第一章节&#xff1a;计…

重磅!OpenAI发布GPT-4o,非常惊艳语音版ChatGPT!

5月15日凌晨&#xff0c;谷歌召开“ I/O 2024”&#xff0c;生成式AI成为本次大会的重点并发布了一系列产品和多款大模型。 其中&#xff0c;谷歌DeepMind发布了一款全新的AI 代理&#xff08;Agent&#xff09;产品Project Astra&#xff0c;可以像昨天OpenAI发布的GPT4o一样…

springsecurity项目快速搭建

自定义security的搭建 package com.sangeng.config;import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;Co…

YOLOv8改进教程|加入可改变核卷积AKConv模块,效果远超DSConv!

⭐⭐ YOLOv8改进专栏|包含主干、模块、注意力机制、检测头等前沿创新 ​ ⭐⭐ 一、 论文介绍 论文链接&#xff1a;https://arxiv.org/abs/2311.11587 代码链接&#xff1a;GitHub - CV-ZhangXin/AKConv 论文速览&#xff1a;&#xff1a;AKConv是2023年11月发表的一种可变卷积…

详细分析Vue3中的reactive(附Demo)

目录 1. 基本知识2. 用法3. Demo 1. 基本知识 reactive 是一个函数&#xff0c;用于将一个普通的 JavaScript 对象转换为响应式对象 当对象的属性发生变化时&#xff0c;Vue 会自动追踪这些变化&#xff0c;并触发相应的更新 Vue2没有&#xff0c;而Vue3中有&#xff0c;为啥…

C++入门系列-赋值运算符重载

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 赋值运算符重载 运算符重载 C为了增强代码的可读性引入了运算符重载&#xff0c;运算符重载是具有特殊函数名的函数&#xff0c;也具有其返回值类型&#xff0c;函数名字以及参…

评价决策类-层次分析法

师从江北 问题引出 归一化处理&#xff1a;指标的数组[a b c]归一化处理得到[a/(abc),b/(abc),c/(abc)] 因为每个指标的重要性不同&#xff0c;所以要加上一个权重 如何科学的确定权重&#xff0c;就要用到层次分析法&#xff08;AHP&#xff09; 模型原理 建立递阶层次结构模…

百度云防护如何开启CC攻击防护

百度云防护的最重要的功能是可以CC攻击防护&#xff0c;针对CC攻击&#xff0c;百度云防护有被动的CC攻击拦截规则&#xff0c;也有主动自定义访问策略拦截。 今天百度云来教大家如何开启百度云防护的CC攻击防御功能。 1.进入防护模板功能-创建模板 2.开启CC攻击防御功能&…

ubuntu20.04 ROS 环境下使用速腾80线激光雷达

1.相关系统环境 系统版本:ubuntu 20.04 ROS版本&#xff1a;ROS1 - noetic 激光雷达型号&#xff1a;RoboSense Ruby &#xff08;更新于2024.5.14&#xff09; 2.网口配置&#xff1a; 将PC/工控机的网口配置为&#xff1a; ipv4&#xff0c;方式设置为手动 ip地址、掩码以…

半小时搞懂STM32知识点——UART

1.UART 1.1为什么要使用UART这种协议?介绍一下UART及其特点 成本低&#xff0c;硬件简单&#xff0c;数据格式灵活&#xff1b; 低速全双工异步串行通信 1.2 UART数据帧格式&#xff1f; 起始位&#xff08;1&#xff09;&#xff0b;数据位&#xff08;5-8&#xff09; 校验位…

保研机试之【execve函数】

execve 参考&#xff1a;fork&#xff08;&#xff09;函数两次返回_fork是如何返回两次的-CSDN博客 setjmp/longjmp 还有E&#xff1a;

解决kali Linux2024无法获取动态IPv4地址(DHCP)解决方案

用root用户启动终端 进入根目录&#xff0c;选择配置文件 cd到根目录下/../etc/network找到interfaces文件 编辑interfaces文件 vi interfaces&#xff0c;编辑interfaces文件 输入如下命令 打开虚拟网络编辑器 选择虚拟机选项卡&#xff0c;编辑&#xff0c;打开虚拟网络编…

数据结构——二叉树知识点详解!

引言&#xff1a;本篇博客将详细介绍到数据结构中的又一位大将——二叉树。它也是我们目前学到的第一个非线性的数据结构。并且本章将学到的概念居多&#xff0c;希望大家可以理解并牢记。 更多有关C语言和数据结构知识详解可前往个人主页&#xff1a;计信猫 目录 一&#xff0…

C++语法|对象的浅拷贝和深拷贝

背景&#xff1a; 我们手写一个顺序栈&#xff0c;展开接下来的实验&#xff1a; ⭐️ this指针指向的是类在内存中的起始位置 class SeqStack { public:SqeStack(int size 10) {cout << this << "SeqStack()" << endl;pstack_ new int[size_];t…

Gemini 5.14日更新 - 推出Gemini Advance服务

收到Gemini Advance试用邀请 今天和往常一样&#xff0c;打开Gemini&#xff0c;惊喜的发现右小角一行小字&#xff1a;试用Gemini Advance。好家伙&#xff0c;OpenAI 刚推出ChatGPT 4o&#xff0c;Google立马推出Gemini Advance&#xff0c;说明国外高科技企业也是很拼的。 …