二叉树的顺序结构(堆的实现)

news2024/10/5 16:20:48

前言

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

1.堆的概念及结构

如果有一个关键码的集合 K = { k0, k1,k2,…,k(n-1)},把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki <=K(2*i+1)且Ki<=K(2*i+2)(Ki >=K(2*i+1)且Ki>=K(2*i+2)  i =0 1 , 2…,则称为小堆 (或大堆) 。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。(这里的数字都是对应的下标
堆的性质:
堆中某个结点的值总是不大于或不小于其父结点的值;
堆总是一棵完全二叉树。

2.堆的创建和功能实现

2.1堆的基本结构的创建和相关函数声明。

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

typedef int  Datatype;
typedef  struct Heap {
	Datatype* a;
	int size;
	int capacity;
}HP;
// 堆的初始化
void HeapInit(HP* hp);
// 堆的销毁
void HeapDestory(HP* hp);
// 堆的插入
void HeapPush(HP* hp, Datatype x);
// 堆的删除
void HeapPop(HP* hp);
// 取堆顶的数据
Datatype HeapTop(HP* hp);
// 堆的数据个数
bool HeapSize(HP* hp);
// 堆的判空
int HeapEmpty(HP* hp);
//向上调整
void Adjustup(Datatype* a, int child);
//向下调整
void Adjustdown(Datatype* a, int n, int parent);
//数据交换
void Swap(Datatype* p1, Datatype* p2);

2.2 各函数的实现与讲解

2.1堆的初始化和销毁

堆的初始化和销毁与以前的动态数组实现顺序表和栈的初始化和销毁基本是一样的,在这里小编就不多解释了。

// 堆的初始化
void HeapInit(HP* hp) {
	assert(hp);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}
// 堆的销毁
void HeapDestory(HP* hp) {
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}
2.2堆数据的插入
补充向上调整法
随便给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根结点左右子树不是堆,我们怎么调整呢?
向上调整法
通过比较新插入元素与其父节点的值来判断是否需要进行交换。如果新插入元素的值大于父节点的值,就将它们进行交换,并更新索引值。这样,逐步向上调整,直到新插入元素找到了合适的位置,保证了堆的性质。
//向上调整
void Adjustup(Datatype* 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* hp, Datatype x) {
	assert(hp);
	if (hp->size == hp->capacity) {
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		Datatype* temp = (Datatype*)realloc(hp->a, sizeof(Datatype) * newcapacity);
		if (temp == NULL) {
			perror("realloc fail");
			return;
		}
		hp->a = temp;
		hp->capacity = newcapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;
	Adjustup(hp->a, hp->size-1);
}

例如数组a[]={1, 5, 3, 8, 7, 6},依次插入并建立大堆后的顺序是:

  1. 插入 1,堆变为 [1]
  2. 插入 5,堆变为 [5, 1]
  3. 插入 3,堆变为 [5, 1, 3]
  4. 插入 8,堆变为 [8, 5, 3, 1]
  5. 插入 7,堆变为 [8, 7, 3, 1, 5]
  6. 插入 6,堆变为 [8, 7, 6, 1, 5, 3]

所以,建立大堆后的顺序是 [8, 7, 6, 1, 5, 3]。

2.3堆的删除
补充向下调整法

在这里惯性思维是直接删除头顶数据,然后重新建堆,通过向上调整法,但是我们需要从最后一个元素依次遍历向上调整。

这里我们采用向下调整法。

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
本张图是小堆的删除。大堆的删除方式是一样的。
因为堆数据插入是建立大堆的,所以这里的代码是大堆的向下调整。
//向下调整
void Adjustdown(Datatype* a, int n, int parent) {
	//假设法,假设左孩子大
	int child = parent * 2 + 1;
	
	while (child < n ) {
		if (child + 1 < n && a[child + 1] > a[child])//a[child+1]<a[child]
			child = child + 1;
		if (a[child] > a[parent]) {  //a[child]<a[parent]
			Swap(&a[child], &a[parent]);
		   parent = child;
		    child = parent * 2 + 1;
	}
		else break;
		
	}
}

堆的删除

// 堆的删除
void HeapPop(HP* hp) {
	assert(hp);
	assert(hp->size > 0);
	Swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	Adjustdown(hp->a, hp->size, 0);
}

最后我们会发现删除的数据是从大到小排列的,这里就可以牵扯到堆排序的应用,小编在下节会讲解的。

2.4其他函数实现(交换,判空,堆顶数据,数据个数)
//数据交换
void Swap(Datatype* p1, Datatype* p2) {
	Datatype temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
// 取堆顶的数据
Datatype HeapTop(HP* hp) {
	assert(hp);
	assert(hp->size > 0);
	return hp->a[0];
}
// 堆的数据个数
int HeapSize(HP* hp) {
	assert(hp);
	return hp->size;
}
// 堆的判空
bool HeapEmpty(HP* hp) {
	assert(hp);
	return hp->size == 0;

}

3.代码测试

#include "Heap.h"
void TestHeap1()
{
	int a[] = { 4,2,8,1,5,6,9,3,2,23,55,232,66,222,33,7,66,3333,999 };
	//int a[] = { 1,5,3,8,7,6 };
	HP hp;
	HeapInit(&hp);
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		HeapPush(&hp, a[i]);
	}
	printf("堆的大小为%d\n", HeapSize(&hp));

	int i = 0;
	while (!HeapEmpty(&hp))
	{
		printf("%d ", HeapTop(&hp));
		//a[i++] = HPTop(&hp);
		HeapPop(&hp);
	}
	printf("\n");
}
/*
	// 找出最大的前k个
	int k = 0;
	scanf("%d", &k);
	while (k--)
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
	printf("\n");

	HeapDestory(&hp);
}*/
int main(){
TestHeap1();
return 0;
}

4.堆的选择题(方便大家理解)

1. 下列关键字序列为堆的是:(A)
A 100 , 60 , 70 , 50 , 32 , 65             B 60 , 70 , 65 , 50 , 32 , 100             C 65 , 100 , 70 , 32 , 50 , 60
D 70 , 65 , 100 , 32 , 50 , 60             E 32 , 50 , 100 , 70 , 65 , 60             F 50 , 100 , 70 , 65 , 60 , 32
2. 已知小根堆为 8 , 15 , 10 , 21 , 34 , 16 , 12 ,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次数是(C)。
A 1                      B 2                        C 3                        D 4
3. 最小堆 [ 0 , 3 , 2 , 5 , 7 , 4 , 6 , 8 ], 在删除堆顶元素 0 之后,其结果是(C)
A [ 3 2 5 7 4 6 8 ]                  B [ 2 3 5 7 4 6 8 ]
C [ 2 3 4 5 7 8 6 ]                  D [ 2 3 4 5 6 7 8 ]

本节内容到此结束,下次小编将讲解堆排序的知识,欢迎大家捧场!!!

期待各位友友的三连和评论!!!

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

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

相关文章

Unity3D获得服务器时间/网络时间/后端时间/ServerTime,适合单机游戏使用

说明 一些游戏开发者在做单机游戏功能时&#xff08;例如&#xff1a;每日奖励、签到等&#xff09;&#xff0c;可能会需要获得服务端标准时间&#xff0c;用于游戏功能的逻辑处理。 问题分析 1、自己如果有服务器&#xff1a;自定义一个后端API&#xff0c;客户端按需请求…

性能狂飙:SpringBoot应用优化实战手册

在数字时代&#xff0c;速度就是生命&#xff0c;性能就是王道&#xff01;《极速启航&#xff1a;SpringBoot性能优化的秘籍》带你深入SpringBoot的内核&#xff0c;探索如何打造一个飞速响应、高效稳定的应用。从基础的代码优化到高级的数据库连接池配置&#xff0c;再到前端…

怎么用PHP语言实现远程控制两路照明开关

怎么用PHP语言实现远程控制两路开关呢&#xff1f; 本文描述了使用PHP语言调用HTTP接口&#xff0c;实现控制两路开关&#xff0c;两路开关可控制两路照明、排风扇等电器。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称厂商1智能WiFi墙…

WPF国际化的最佳实践

WPF国际化的最佳实践 1.创建项目资源文件 如果你的项目没有Properties文件夹和Resources.resx文件&#xff0c;可以通过右键项目-资源-常规-添加创建或打开程序集资源 2.添加国际化字符串 打开Resources.resx文件&#xff0c;添加需要翻译的文本字符&#xff0c;并将访问修…

Go 1.19.4 切片与子切片-Day 05

1. 切片 1.1 介绍 切片在Go中是一个引用类型&#xff0c;它包含三个组成部分&#xff1a;指向底层数组的指针&#xff08;pointer&#xff09;、切片的长度&#xff08;length&#xff09;以及切片的容量&#xff08;capacity&#xff09;&#xff0c;这些信息共同构成了切片的…

el-input添加clearable属性 输入内容时会直接撑开

<el-inputclearablev-if"item.type number || item.type text":type"item.type":placeholder"item.placeholder":prefix-icon"item.icon || "v-model.trim"searchform[item.prop]"></el-input>解决方案 添加c…

inflight 守恒拥塞控制的稳定性

只要系统形成 E_best max(bw / delay) 共识&#xff0c;系统就是稳定的。 设两条流 f1&#xff0c;f2 共享瓶颈链路&#xff0c;用 cwnd 约束 inflight&#xff0c;其 cwnd 分别为 x&#xff0c;y&#xff0c;用简单的微分方程建模&#xff1a; d x d t c − b ∗ x − a ∗…

TCP/IP(网络编程)

一、网络每一层的作用 &#xff0a;网络接口层和物理层的作用&#xff1a;屏蔽硬件的差异&#xff0c;通过底层的驱动&#xff0c;会提供统一的接口&#xff0c;供网络层使用 &#xff0a;网络层的作用&#xff1a;实现端到端的传输 &#xff0a;传输层:数据应该交给哪一个任…

区块链(Blockchain)调查研究

文章目录 1. 区块链是什么&#xff1f;2. 区块链分类和特点3. 区块链核心关键技术3.1 共识机制3.2 密码学技术3.4 分布式存储3.5 智能合约 4. 区块链未来发展趋势5. 区块链 Java 实现小案例 1. 区块链是什么&#xff1f; 区块链是分布式数据存储、点对点传输、共识机制、加密算…

TPC-H建表语句(MySQL语法)

TPC-H测试集介绍 TPC-H&#xff08;Transaction Processing Performance Council, Standard Specification, Decision Support Benchmark, 简称TPC-H&#xff09;是一个非常权威数据库基准测试程序&#xff0c;由TPC组织制定。 TPC-H定义了一个包含8个表的模式&#xff08;Sc…

Github上一款开源、简洁、强大的任务管理工具:Condution

Condution 是一款开源任务管理工具&#xff0c;它以简洁易用、功能强大著称。它旨在为用户提供一个简单高效的平台&#xff0c;帮助他们管理日常任务、提高工作效率。 1. Condution 的诞生背景 现如今&#xff0c;市面上存在着许多任务管理软件&#xff0c;但它们往往价格昂贵…

芒果YOLOv8改进169:即插即用 | 秩引导的块设计核心CIB结构,设计一种秩引导的块设计方案,旨在通过紧凑型架构设计减少被显示为冗余的阶段的复杂性

💡🚀🚀🚀本博客 秩引导的块设计,设计了一种秩引导的块设计方案,旨在通过紧凑型架构设计减少被显示为冗余的阶段的复杂性 :内含源代码改进 适用于 YOLOv8 按步骤操作运行改进后的代码即可 文章目录 即插即用|秩引导的块设计|最新改进 YOLOv8 代码改进论文理论YOLO…

山洪灾害监测预警系统守护生命安全的新利器

一、概述 我国地域辽阔&#xff0c;地形地貌条件复杂且气候类型多样&#xff0c;每年5—9月为山洪灾害多发期&#xff0c;6—8月主汛期山洪及次生地质灾害更为集中。由于地理位置、气候条件、地貌特征、社会经济发展水平等成灾环境的不同&#xff0c;山洪灾害在发生时间、空间和…

Mixly UDP局域网收发数据

一、开发环境 软件&#xff1a;Mixly 2.0在线版 硬件&#xff1a;ESP32-C3&#xff08;立创实战派&#xff09; 固件&#xff1a;ESP32C3 Generic(UART) 测试工具&#xff1a;NetAssist V5.0.1 二、实现功能 ESP32作为wifi sta连接到路由器&#xff0c;连接成功之后将路由器…

仅49天!中科院2区SCI,发文量超2W,征稿范围广!

【欧亚科睿学术】 &#xff08;一&#xff09;期刊简介概况 【出版社】SPRINGER出版社 【期刊概况】IF&#xff1a;4.0-5.0&#xff0c;JCR2区&#xff0c;中科院2区 【版面类型】正刊&#xff0c;仅10篇版面 【预警情况】2020-2024年无预警记录 【收录年份】2008年被WOS…

【git】TortoiseGitPlink Fatal Error 解决方法

背景 使用 TortoiseGit报错&#xff1a; TortoiseGitPlink Fatal Error No supported authentication methods available (server sent: publickey) 解决方法 1、有很多是重置git的秘钥解决的 2、重置ssh工具

如何理解与学习数学分析——第二部分——数学分析中的基本概念——第8章——可微性

第2 部分&#xff1a;数学分析中的基本概念 (Concepts in Analysis) 8. 可微性(Differentiability) 本章讨论梯度(gradients)/斜率(slopes)和切线(tangent)&#xff0c;指出常见的误解并解释如何避免这些误解。将可微性的定义与图形表示联系起来&#xff0c;展示如何将其应用…

[leetcode hot 150]第一百三十七题,只出现一次的数字Ⅱ

题目&#xff1a; 给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。 由于需要常数级空间和线性时间复杂度…

论文Compiler Technologies in Deep Learning Co-Design: A Survey分享

目录 标题摘要引言背景深度学习软件和硬件的发展不同时期的协同设计深度学习协同设计系统神经网络架构设计和优化协同设计技术 用于协同设计的深度学习系统中的编译技术深度学习编译器TVM 生态系统和MLIR生态系统IR转换和优化代码生成运行时和执行模式 Buddy-Compiler: 一个针对…

[C++]基于C++opencv结合vibe和sort tracker实现高空抛物实时检测

【vibe算法介绍】 ViBe算法是一种高效的像素级视频背景建模和前景检测算法。以下是对该算法的详细介绍&#xff1a; 一、算法原理 ViBe算法的核心思想是通过为每个像素点存储一个样本集&#xff0c;利用该样本集与当前像素值进行比较&#xff0c;从而判断该像素是否属于背景…