【数据结构】顺序二叉树的实现—以堆的实现为例、堆的调整、堆的创建、堆的插入和删除、堆排序

news2024/12/23 13:34:14

文章目录

  • 1.堆的概念及结构
  • 2.堆的实现(以大堆为例)
    • 2.1堆的插入
      • 2.1.1堆的向上调整算法
    • 2.2堆的删除
      • 2.2.1堆的向下调整算法
    • 2.3堆的创建
    • 2.4有关建堆的时间复杂度
  • 3.堆排序
  • 4.C语言堆实现源码

1.堆的概念及结构

  堆就是顺序结构二叉树。

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

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

在这里插入图片描述

  我们在构建堆时,要想象堆的逻辑结构进行操作,但实际堆在计算机中是以数组的方式储存的。
  堆的逻辑结构是一颗树,树上的每个节点都有一个值,根据节点之间的大小关系,堆被分为小根堆和大根堆两种类型。堆的储存结构一般采用数组来实现,数组中的元素的排列顺序是按照完全二叉树的顺序来存储的,即从上到下,从左到右的顺序。

在这里插入图片描述

  对于一个大根堆而言,储存结构中数组的第一个元素即为堆中的最大值,小堆则相反,也就是说逻辑上靠近根节点的实际上也是储存结构中靠前的元素
  由于堆的储存结构是按照完全二叉树的形式存储的,因此在进行堆的插入和删除操作时,可以方便的通过数组来进行操作,无需关注堆的具体逻辑结构,即可实现堆的调整和维护。

2.堆的实现(以大堆为例)

2.1堆的插入

  堆的插入操作需要保证插入新数之后仍然满足堆的性质,即每个节点都大于或等于其子节点。插入操作的具体过程如下:

 (1)将新数插入堆的最后一个位置,保持完全二叉树的形状。

 (2)将新数与其父节点比较,如果新数大于其父节点,则交换它们的位置,直到新数不再大于其父节点或者到达堆的根节点。

在这里插入图片描述

2.1.1堆的向上调整算法

  AdjustUp函数实现了从下向上调整堆的结构,使得满足堆的性质,具体实现过程如下:

(1)从子节点child开始,计算其父节点parent的位置,parent的位置为(child - 1) / 2

(2)如果子节点比父节点的值大,则交换两个节点的值,否则结束调整;

(3)将child和parent向上移动,继续比较和交换它们的值,直到child小于它的父节点或者已达到堆的根部(即parent为0)。

  这个过程也被称为上滤(Percolate Up)操作,用于添加一个新的元素时,将新元素添加到堆的底部,然后执行上滤操作,将新元素放到合适的位置,以满足堆的性质。

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;
		}
	}
}

2.2堆的删除

  堆的删除操作需要保证删除后仍然满足堆的性质,即每个节点都大于或等于其子节点。删除操作的具体过程如下:

(1)将堆顶元素与堆中最后一个元素进行交换。

(2)删除堆中最后一个元素。

(3)将新的堆顶元素与其子节点比较,如果新堆顶元素小于其子节点,则将其与子节点中较大的一个交换,直到新的堆顶元素不再小于其子节点或到达堆的底部。
在这里插入图片描述

2.2.1堆的向下调整算法

  这段代码实现了从上向下调整堆的结构,使得满足堆的性质,具体实现过程如下:

(1)从父节点parent开始,计算其左子节点child的位置为parent*2+1

(2)如果父节点的值比其子节点中的较小值还小,则将两个节点的值交换;

(3)将parent向下移动到child的位置,计算新的child的索引;

(4)重复上述2、3步,直到parent比其子节点的值都小或者已经到达堆的底部。

  这个过程也被称为下滤(Percolate Down)操作,用于删除堆顶元素后重新调整堆的结构。

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;
		}
	}
}

2.3堆的创建

  现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

//这里以小堆为例
int array[] = {27,15,19,18,28,34,65,49,25,37};

在这里插入图片描述

2.4有关建堆的时间复杂度

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

  因此:建堆的时间复杂度为O(N)

3.堆排序

  堆排序即利用堆的思想来进行排序,总共分为两个步骤:
(1)建堆(升序:建大堆 降序:建小堆)
(2)利用堆删除思想来进行排序
建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。
在这里插入图片描述
现在只是简单介绍一下,以后我们会对堆排序进行详细的介绍。

4.C语言堆实现源码

Heap.h

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

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

void HeapPrint(HP* php);
void HeapInit(HP* php);
void HeapDestroy(HP* php);

void HeapPush(HP* php, HPDataType x);
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);
int HeapSize(HP* php);

void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent);

void Swap(HPDataType* p1, HPDataType* p2);


Heap.c

#include"Heap.h"

void HeapPrint(HP* php)
{
	assert(php);

	if (php->a == NULL)
	{
		printf("该堆为空堆\n");
	}
	else
	{
		printf("该堆中的元素是:");

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

		printf("\n");
	}
}

void HeapInit(HP* php)
{
	assert(php);

	php->a = (HPDataType*)malloc(sizeof(HPDataType) * 4);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}

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

void HeapDestroy(HP* php)
{
	assert(php);

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


void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType x = *p1;
	*p1 = *p2;
	*p2 = x;
}

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(HP* php, HPDataType x)
{
	assert(php);

	if (php->capacity == php->size)
	{
		HPDataType*tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * php->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		php->a = tmp;
		php->capacity *= 2;
	}

	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(!HeapEmpty(php));

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

	AdjustDown(php->a, php->size, 0);
}

HPDataType HeapTop(HP* php)
{
	assert(php);
	return php->a[0];
}

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}


Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"

void HeapTest1()
{
	HP hp;
	HeapInit(&hp);
	HeapPrint(&hp);

	HeapPush(&hp, 12);
	HeapPush(&hp, 4);
	HeapPrint(&hp);


	HeapPush(&hp, 2);
	HeapPush(&hp, 22);
	HeapPush(&hp, 18);
	HeapPrint(&hp);

	HeapPush(&hp, 6);
	HeapPush(&hp, 10);
	HeapPush(&hp, 54);
	HeapPush(&hp, 43);
	HeapPrint(&hp);
}

int main()
{
	HeapTest1();
	return 0;
}

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

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

相关文章

操作系统第四章——文件管理(上)

凡所有相&#xff0c;皆是虚妄&#xff0c;若见诸相非相&#xff0c;即见如来 文章目录 4.1.1 初识文件管理前情回顾文件的属性文件内部的数据应该怎样组织起来&#xff1f;文件之间应该怎么样组织起来&#xff1f;操作系统应该向上提供那些功能&#xff1f;文件如何存放在外存…

JOSEF约瑟 JDZY-1440 DC110V 静态中间继电器 导轨安装 常用于电力系统

名称&#xff1a;中间继电器&#xff1b;型号&#xff1a;JDZY-1440&#xff1b;品牌&#xff1a;JOSEF约瑟&#xff1b;额定电压&#xff1a;24,48,110,220VDC/AC380VAC&#xff1b;触点容量&#xff1a;250VAC/5A&#xff1b;功率消耗&#xff1a;2W/3.5W&#xff1b;返回时间…

图数据库实践 - 如何将图数据库应用于身份与访问管理

导读 目前&#xff0c;随着云计算和大数据的快速发展&#xff0c;身份与访问管理&#xff08;Identity and Access Management&#xff0c;IAM&#xff09;系统变得比以往任何时候都更加重要。因为涉密信息可能在几分钟内就被破解&#xff0c;网络犯罪分子仅需要一个员工账号&…

【敬伟ps教程】蒙版和通道的基础知识

文章目录 通道通道面板 Alpha 通道通道和选区的关系编辑 Alpha通道原色通道的利用 图层蒙版编辑图层蒙版快速蒙版 通道 通道是图像文件的一种颜色数据信息存储形式&#xff0c;它与图像文件的颜色模式密切相关 多个分色通道(如图:红R、绿G、蓝B)叠加在一起可以组成一幅具有颜…

通过Python的PIL库给图片添加马赛克

文章目录 前言一、Pillow是什么&#xff1f;二、安装PIL库三、查看PIL库版本四、使用方法1.引入库2.定义图片路径3.打开需要打马赛克的图片4.获取图片尺寸5.创建一个新的图片对象6.定义块的宽高7.循环遍历图片中的每个块进行处理8.保存马赛克图片9.效果 总结 前言 大家好&#…

《Redis-Windows平台下Redis集群的使用》

文章目录 Redis主从集群1.集群结构2.准备实例和配置3.启动4.开启主从关系5.测试Redis主从集群 win-redisx64下载地址 :https://github.com/microsoftarchive/redis/releases 1.集群结构 我们搭建的主从集群结构如图: 共包含三个节点,一个主节点,两个从节点。 这里我们…

内网远程桌面控制软件推荐

远程控制企业内部网中的计算机&#xff08;没有连接外网&#xff09;&#xff0c;如果控制端和被控端都处在该内网&#xff0c;您可以按照以下步骤操作&#xff1a; 1、确保两台计算机都连接到同一个局域网 确保要控制的计算机和要启动远程控制的计算机连接到企业内的同一个局…

python知识点100篇系列(11)-浮点数四舍五入的两种方法

Python 的四舍五入主要有两种方式; 内置函数 round(number[, ndigits])使用 Decimal先说结论: 如果是对金额的四舍五入,不建议使用内置函数,原因如下: 使用round方法: python3中的round函数对浮点数进行四舍五入的规则: 参数ndigits 不为 0 的情况如果保留位数的后一位…

瑞云科技助力番职院打造虚拟数字人,探索职业教育创新之路

3月24-25日&#xff0c;教育部高等学校科学研究发展中心主办、广州番禺职业技术学院承办的2022-2023年职业教育示范性虚拟仿真实训基地建设工作推进会在中国广州举行。会议旨在指导各院校建好用好管好虚拟仿真示范实训基地&#xff0c;以数字化、网络化、智能化赋能职业教育高质…

SeaTunnel本地运行以及kafka发送到redis说明

下载 Seatunnel2.3.1源码 Idea中的目录结构 编译 通过maven进行代码编译 编译命令 mvn clean package -pl seatunnel-dist -am -Dmaven.test.skiptrue 编译单个模块命令 mvn clean package -pl seatunnel-examples/seatunnel-engine-examples -am -Dmaven.test.skiptrue …

@开源爱好者,字节跳动这项技术,正式宣布开源了

告诉大家一个好消息&#xff0c;字节跳动的云原生数据仓库 ByConity 正式宣布开源了。 ByConity 是一个云原生数据仓库&#xff0c;由字节跳动数据平台团队在国际知名开源数据库管理系统 ClickHouse 社区版本基础上开发。 早期&#xff0c;字节跳动的数据存储使用的是 ClickHou…

【Axure教程】通过文本框维护下拉列表选项

下拉列表&#xff08;Dropdown List&#xff09;是一种常见的用户界面元素&#xff0c;用于提供一组选项供用户选择。它通常以一个展开的列表形式出现&#xff0c;用户可以点击或选择列表中的一个选项。一般来说&#xff0c;他的选项值是由系统代码组成的&#xff0c;所以一般是…

leecode每日一题 1080 根到叶路径上的不足节点

题目描述 给你二叉树的根节点 root 和一个整数 limit &#xff0c;请你同时删除树中所有 不足节点 &#xff0c;并返回最终二叉树的根节点。 假如通过节点 node 的每种可能的 “根-叶” 路径上值的总和全都小于给定的 limit&#xff0c;则该节点被称之为 不足节点 &#xff0…

接口测试神器, ApiKit ,亲测好用

自己关注的公众号比较多&#xff0c;之前有收到过有关 ApiKit 的文章&#xff0c;自己也是大致看看,还没有用过它&#xff01; 最近看到比较多有关 ApiKit 的文章&#xff0c;所以自己就花了点时间去研究它&#xff0c;使用完后发现确实比Postman功能更强大&#xff0c;界面也…

Redis7实战加面试题-基础篇(Redis复制(replica),Redis哨兵(sentinel),Redis集群(cluster))

Redis复制(replica) 就是主从复制&#xff0c;master以写为主&#xff0c;Slave以读为主。当master数据变化的时候&#xff0c;自动将新的数据异步同步到其它slave数据库。 能干嘛&#xff1a;读写分离&#xff0c;容灾恢复&#xff0c;数据备份&#xff0c;水平扩容支撑高并发…

【阿里云】阿里云OSS对象存储— 开通OSS服务、搭建OSS环境、快速入门

目录 一、开通OSS服务 二、搭建OSS环境 1、创建Bucket存储空间 2. 创建文件夹上传图片 3. RAM 访问控制 三、快速入门 1.下载SDK 2.创建存储空间[可选] 3.上传图片 一、开通OSS服务 二、搭建OSS环境 1、创建Bucket存储空间 选择 Bucket列表&#xff0c;进行Bucket创建…

LeetCode54 螺旋矩阵

给你一个m行n列的矩阵matrix&#xff0c;请按照顺时针螺旋顺序&#xff0c;返回矩阵中的所有元素。 示例1 输入&#xff1a; matrix [[1,2,3],[4,5,6],[7,8,9] 输出&#xff1a; [1,2,3,6,9,8,7,4,5] 示例2 输入&#xff1a; matrix [[1,2,3,4],[5,6,7,8],[9,10,11,12]] 输出…

新冠又临“涨潮”期,看我AI显身手

#疫情#北京时间5月24日&#xff0c;北京市卫生健康委发布2023年第20周疫情周报。报告显示&#xff0c;2023年第20周全市共报告法定传染病14种25544例&#xff0c;死亡1例。报告病例数居前5位的病种依次为:新型冠状病毒感染、其它感染性腹泻病、流行性感冒、肺结核和病毒性肝炎&…

chatgpt赋能python:Python的shutil模块:探讨删除文件和目录的方法

Python的shutil模块&#xff1a;探讨删除文件和目录的方法 Python是一种高级编程语言&#xff0c;拥有大量的标准库&#xff0c;其中shutil模块是处理文件的一种非常强大的模块。shutil提供了多种方式来复制、移动和删除文件和目录。 在本文中&#xff0c;我们将探讨Python的…

英特尔13代桌面CPU平台安装ubuntu20.04LTS记录

安装环境一&#xff1a; i7-13700K 华硕Z790-P 16X2GB内存 华硕 TUF 3070 O8G安装环境二&#xff1a; i5-13400 华硕B760M-A D4 8x2GB内存 UHD730核显安装系统&#xff1a; ubuntu20.04LTS(首先用光驱安装失败&#xff0c;后面用U盘更换ubuntu22.04.2LTS成功) 因为…