数据结构7——二叉树的顺序结构以及堆的实现

在上篇文章数据结构6——树与二叉树中,我们了解了树和二叉树的概念,接着上篇文章,在本篇文章中我们学习二叉树顺序结构的实现。



目录

1. 二叉树的顺序存储结构

2. 堆的概念及结构

1. 堆的概念

2. 堆的结构

3. 堆的实现

1. 堆节点

2. 交换节点

3. 打印

4. 堆的插入

向上调整:

插入:

5. 堆的删除

向下调整:

删除:

6. 初始化

7. 销毁

验证:

源代码:

Heap.h:

Heap.c:

test.c:


1. 二叉树的顺序存储结构

二叉树一般可以使用两种结构存储,一种是顺序结构,一种是链式结构。本篇文章我们先来研究二叉树的顺序存储,下篇文章详解二叉树的链式存储。

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

如图: 完全二叉树顺序存储:

非完全二叉树顺序存储:

2. 堆的概念及结构

1. 堆的概念

简单来说,堆除了是一棵完全二叉树之外,还要满足堆序性。

堆中每个节点的值都大于等于其子节点的值,根节点是堆中最大的元素,这样的堆称为最大堆或大根堆;

相反的,堆中每个节点的值都小于等于其子节点的值,根节点是堆中最小的元素,这样的堆称为最小堆或小根堆;

2. 堆的结构

3. 堆的实现

(本篇文章只实现最小堆,最大堆与最小堆是相同的道理)

1. 堆节点

typedef int HPDataType;
//堆的物理结构与顺序表相似
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

2. 交换节点

//交换
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

3. 打印

//打印
void HeapPrint(HP* php)
{
	assert(php);

	for (size_t i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

4. 堆的插入

可是现在堆属性并不满足,因为30在5的上面,这是一个最小堆,我们需要将小的数字在上面。

为了恢复堆属性,我们需要交换30和5:

可是现在仍旧没有满足最小堆的堆属性,所以还需要交换10和5:

此时才得到了最小堆。

因此在插入数据后每次都要进行向上调整,于是向上调整的实现为:

向上调整:

//向上调整
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 = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

插入:

//插入
void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	//判断是否需要扩容
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * 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);//传数组的最后一个数据
}

5. 堆的删除

由于堆顶是最大或最小元素,为了满足堆属性,所以堆中每次只能删除堆顶元素,一般的做法是先将堆顶元素与数组末尾元素先交换,再删除末尾元素。

可是现在40在了堆顶,反而成了最大的元素,并不满足最小堆,这时就需要向下调整:

每次删除都要调整,所以向下调整的具体实现为:

向下调整:

//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//找出小的那个孩子
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			//继续往下调整
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

删除:

//删除
void HeapPop(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);
}

6. 初始化

//初始化
void HeapInit(HP* php)
{
	assert(php);

	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}

7. 销毁

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

	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}

验证:

int main()
{
	int a[] = { 9,3,7,10,24,14,28,72,21,5 };
	HP hp;
	HeapInit(&hp);
	for (size_t i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		HeapPush(&hp, a[i]);
	}
	HeapPrint(&hp);
	HeapDestroy(&hp);
	return 0;
}



源代码:

Heap.h:

#pragma once

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

typedef int HPDataType;
//堆的物理结构与顺序表相似
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

//交换
void Swap(HPDataType* p1, HPDataType* p2);

//打印
void HeapPrint(HP* php);

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

//向上调整
void AdjustUP(HPDataType* a, int child);
//插入
void HeapPush(HP* php, HPDataType x);


//向下调整
void AdjustDown(HPDataType* a, int n, int parent);
//删除
void HeapPop(HP* php);

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

Heap.c:

#include "Heap.h"

//交换
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//打印
void HeapPrint(HP* php)
{
	assert(php);

	for (size_t i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

//向上调整
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 = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//插入
void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	//判断是否需要扩容
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * 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 AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//找出小的那个孩子
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			//继续往下调整
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//删除
void HeapPop(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);
}

//初始化
void HeapInit(HP* php)
{
	assert(php);

	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}

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

	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}

test.c:

int main()
{
	int a[] = { 9,3,7,10,24,14,28,72,21,5 };
	HP hp;
	HeapInit(&hp);
	for (size_t i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		HeapPush(&hp, a[i]);
	}
	HeapPrint(&hp);
	HeapDestroy(&hp);
	return 0;
}

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

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

相关文章

《献给阿尔吉侬的花束》

这是看过的错别字最多的一本书&#xff0c;错别字多并不是这本书的缺点&#xff0c;反而是一个亮点。全书以“近步抱告”的形式讲述了想变“聪明”的查理的故事。很治愈&#xff0c;也很虐心。聪明有聪明的代价&#xff0c;看到的感受到的越多&#xff0c;需要强大的内心去承受…

LeetCode 精选 75 回顾

目录 一、数组 / 字符串 1.交替合并字符串 &#xff08;简单&#xff09; 2.字符串的最大公因子 &#xff08;简单&#xff09; 3.拥有最多糖果的孩子&#xff08;简单&#xff09; 4.种花问题&#xff08;简单&#xff09; 5.反转字符串中的元音字母&#xff08;简单&a…

高性能 JSON 处理:为何选择 Fastjson?

一、关于Fastjson 1.1 简介 Fastjson 是由阿里巴巴集团开发的一个高性能的 JSON 处理库&#xff0c;它支持 Java 对象与 JSON 字符串之间的互相转换。Fastjson 自 2011 年发布以来&#xff0c;以其卓越的性能和丰富的功能在 Java 社区中获得了广泛的应用。 Alibaba Fastjson:…

RabbitMQ系列学习笔记(九)--路由模式

文章目录 一、路由模式原理二、多重绑定三、路由模式实战1、消费者代码2、生产者代码3、运行结果分析 本文参考 尚硅谷RabbitMQ教程丨快速掌握MQ消息中间件rabbitmq RabbitMQ 详解 Centos7环境安装Erlang、RabbitMQ详细过程(配图) 一、路由模式原理 使用发布订阅模式时&#x…

C++ -string -常见用法5

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【C】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 文章目录 &#x1f4a1;前言&#x1f4a1;非成员函数1.operator1.1函数原型1.2使用1.3注意 2.relational operators3.swap4.operator>>5.operator<<6.operator<…

Javascript算法(滑块窗口、螺旋矩阵)

滑块窗口 JS滑块窗口算法&#xff0c;即滑动窗口算法&#xff08;Sliding Window&#xff09;&#xff0c;在JavaScript中的应用场景主要集中在处理字符串和数组等数据结构中的子串或子数组问题。这种算法通过维护一个窗口&#xff0c;并移动窗口的两个边界&#xff08;左右指…

Linux命令进阶·vi\vim编辑器详细命令介绍

目录 1. 什么是 vim&#xff1f; 2. vi\vim 模式介绍 2.1 命令模式&#xff08;Command mode&#xff09; 2.2 输入模式&#xff08;Insert mode&#xff09; 2.3 底线命令模式&#xff08;Last line mode&#xff09; 3. vi\vim 的使用 4. 命令介绍 1. 什么是 …

微信小程序-自定义组件

文章目录 微信小程序-自定义组件概述创建和使用数据、方法和属性slot 插槽默认插槽具名插槽 组件样式注意项样式隔离 数据监听组件间通信父传子子传父获取子组件实例 生命周期组件的生命周期组件所在页面的生命周期App、Page与Component生命周期对比冷启动保留当前页面和关闭当…

诺奖印证产业方向,AI先行者晶泰科技开拓黄金赛道

2024年诺贝尔奖揭晓的各奖项中&#xff0c;AI领域无疑成为“最大赢家”。 从诺贝尔物理学奖被授予两名AI科学家&#xff0c;到诺贝尔化学奖表彰三位科学家“用人工智能&#xff08;AI&#xff09;破译蛋白质的密码”&#xff0c;本届诺贝尔奖“含AI量”之高引起市场热议。 值…

如何将 Elasticsearch 与流行的 Ruby 工具结合使用

作者&#xff1a;来自 Elastic Fernando Briano 了解如何将 Elasticsearch 与一些流行的 Ruby 库一起使用。 在这篇博文中&#xff0c;我们将介绍如何将 Elasticsearch 与一些流行的 Ruby 工具结合使用。我们将实现 Ruby 客户端 “入门”指南 中介绍的常用 API。如果你点击该链…

【从零开发Mybatis】引入XNode和XPathParser

引言 在上文&#xff0c;我们发现直接使用 DOM库去解析XML 配置文件&#xff0c;非常复杂&#xff0c;也很不方便&#xff0c;需要编写大量的重复代码来处理 XML 文件的读取和解析&#xff0c;代码可读性以及可维护性相当差&#xff0c;使用起来非常不灵活。 因此&#xff0c…

深度学习:对评论信息的情感分析,建立模型,自动识别评论信息的情绪状态完整代码实现

评论 思考&#xff1a;向模型中传递数据时&#xff0c;需要提前处理好数据 1、目标&#xff1a;将评论内容转换为词向量。 2、每个词/字转换为词向量长度(维度)200 3、每一次传入的词/字的个数是否就是评论的长度? 应该是固定长度&#xff0c;每次传入数据与图像相似…

DIY我的世界磁力方块

引子 小朋友喜欢我的世界&#xff0c;就像当年我那代对俄罗斯方块的执着&#xff0c;考虑电子游戏伤眼睛&#xff0c;所以最近开始给小朋友买磁力方块。 一个将近1元多的价格&#xff0c;催生我DIY的念头。 正文 Freecad图&#xff0c;A,B,C,D处 放磁铁 5.14g 材料费 最后的成…

Axure中继器单选、多选和重置

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;Axure中继器单选、多选和重置 主要内容&#xff1a;根据查询条件&#xff0c;通过单选、多选和重置&#xff0c;从中继器中得到数据 应用场景&…

DockerCompose快速部署Java项目、nginx前端和mysql数据库到centos虚拟机

简介&#xff1a;整理自&#xff1a;SpringCloud微服务开发与实战&#xff0c;java黑马商城项目微服务实战开发&#xff08;涵盖MybatisPlus、Docker、MQ、ES、Redis高级等&#xff09;课程的飞书文档。 DockerCompose介绍 大家可以看到&#xff0c;我们部署一个简单的java项…

stm32实现esp8266连接到TCP服务器(二)未完

1.2 连接到TCP Server 1.2.1 使用网络助手&#xff0c;设立TCP服务器 ​ 编辑 1.2.2 连接服务器 ATCIPSTART"TCP","192.168.1.18",8080 //指令&#xff0c;注意双引号逗号都要半角(英文)输入 CONNECT //结果&#xff1a;成功 OK //结果&#xff1a;成功 …

[C++]ecplise C++新建项目跑hello world

测试通过版本&#xff1a; ecplise-cpp 2024-09 ecplise-cpp 2020-09 【前提】 安装好MinGW环境&#xff0c;实际测试不需要下载什么CDT插件就可以运行了。 步骤&#xff1a; &#xff08;1&#xff09;打开ecplise,选择launch 选择File->New->C/C Project 选择C M…

Java_数组的使用

一、数组的介绍 数组可以存放多个同一类型的数据。数组也是一种数据类型&#xff0c;是引用类型。 即&#xff1a;数&#xff08;数据&#xff09;组&#xff08;一组&#xff09;就是一组数据 二、代码演示 public class Array01 {public static void main(String[] args) …

DMAIC赋能智能家居:解锁未来生活新篇章!

从清晨自动拉开的窗帘&#xff0c;到夜晚自动调暗的灯光&#xff0c;每一处细节都透露着科技的温度与智慧的光芒。而在这场智能革命的浪潮中&#xff0c;DMAIC&#xff08;定义Define、测量Measure、分析Analyze、改进Improve、控制Control&#xff09;作为六西格玛管理的核心方…

React之组件渲染性能优化

关键词&#xff1a; shouldComponentUpdate、PureComnent、React.memo、useMemo、useCallback shouldComponentUpdate 与 PureComnent shouldComponentUpdate 与 PureComnent 用于类组件。虽然官方推荐使用函数组件&#xff0c;但我们依然需要对类组件的渲染优化策略有所了解…