数据结构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;
}

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

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

相关文章

R语言实现logistic回归曲线绘制

方式一&#xff1a;编制函数 x<-rnorm(10000)#设置随机种子 #编写绘图函数代码快 f <- function(x){y 1/(1 exp(-x))plot(x,y)}#sigmoid函数 f(x)​ 方式二&#xff1a;Sigmoid函数代码 x<-rnorm(10000)#设置随机种子 #编写绘图函数代码块 #y<-1/(1exp(-x)) y&…

数据结构-复杂度

复杂度 1.数据结构1.1算法 2.算法效率2.1复杂度的概念 3.时间复杂度3.1大O渐进表示法3.2时间复杂度计算示例3.2.1 示例13.2.2 示例23.2.3 示例33.2.4 示例43.2.5 示例5&#xff1a;3.2.6 示例63.2.7 示例7 4.空间复杂度4.1.1 示例14.1.2 示例2 5.常见复杂度对比6.复杂度算法题6…

【重学 MySQL】六十七、解锁检查约束,守护数据完整性

【重学 MySQL】六十七、解锁检查约束&#xff0c;守护数据完整性 检查约束的基本概念检查约束的语法检查约束的使用场景注意事项示例 在MySQL中&#xff0c;检查约束&#xff08;CHECK&#xff09;是一种用于确保表中数据满足特定条件的约束。 检查约束的基本概念 检查约束用…

考研前所学c语言02(2024/10/16)

1.一个十进制的数转化为二进制的就是不断除二取余&#xff0c;得到的余数从下到上取 比如123&#xff1a; 结果为&#xff1a; 同理其他的十进制转八进制&#xff0c;十六进制就除八&#xff0c;除十六即可 再比如123转十六进制&#xff1a; 因为余数是11&#xff0c;十六进…

【JavaEE初阶】深入理解网络编程—使用UDP协议API实现回显服务器

前言 &#x1f31f;&#x1f31f;本期讲解关于TCP/UDP协议的原理理解~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…

从0开始深度学习(12)——多层感知机的逐步实现

依然以Fashion-MNIST图像分类数据集为例&#xff0c;手动实现多层感知机和激活函数的编写&#xff0c;大部分代码均在从0开始深度学习&#xff08;9&#xff09;——softmax回归的逐步实现中实现过 1 读取数据 import torch from torchvision import transforms import torchv…

查找与排序-交换排序

交换排序是基于“比较”和“交换”两种操作来实现的排序方法 。 由于选择“比较”的基准元素不同&#xff0c;可将交换排序分为以下两种&#xff1a; 冒泡排序快速排序 一、冒泡排序 1.冒泡排序基本思想 因为其实现与气泡从水中往上冒的过程类似而得名。 每一趟的…

基于SpringBoot+Vue+uniapp微信小程序的垃圾分类系统的详细设计和实现(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

Redux (八) 路由React-router、嵌套路由、路由传参、路由懒加载

文章目录 一、React-Router的基本使用1. 安装及基本使用(路由映射配置)2. 路由跳转Link与NavLink3. Navigate导航4. 处理路径不存在的情况 二、嵌套路由三、手动跳转 (类似编程式路由导航)1. 函数式组件2. 类组件实现手动跳转 四、路由传参1. 路径设置占位符(params)2. search传…

Java面试指南:Java基础介绍

这是《Java面试指南》系列的第1篇&#xff0c;本篇主要是介绍Java的一些基础内容&#xff1a; 1、Java语言的起源 2、Java EE、Java SE、Java ME介绍 3、Java语言的特点 4、Java和C的区别和联系&#xff1f; 5、面向对象和面向过程的比较 6、Java面向对象的三大特性&#xff1a…

leetcode30:串联所有单词的字串

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c; 那么 "abcdef…

1. 解读DLT698.45-2017通信规约--预连接响应

国家电网有限公司企业标准&#xff0c;面向对象的用电信息数据交换协议DLT698.45-2017 为提高用电信息采集系统的业务适应性、采集效率、安全性和数据溯源性&#xff0c;规范用电信息数据交换协议的通信架构、数据链路层、应用层、接口类与对象标识&#xff0c;制定本标准。 …

Linux系统:(Linux系统概述与安装)

硬件计算机硬件是指计算机系统中所有物理部件的总称。包括计算机主机、显示器、键盘、鼠标、内存、硬盘、处理器、主板等等。这些硬件部件是计算机系统运行的基础 不管是电脑系统(个人电脑、服务器等)、还是移动端操作系统(手机、平板等)。它的功能就是做为用户和硬件之间的桥梁…

前端求职简历-待补充

当然可以&#xff0c;针对大厂的前端岗位&#xff0c;一个吸引人的简历应该突出你的技术能力、项目经验、教育背景以及任何能体现你学习能力和团队协作能力的证明。以下是一个简历大纲示例&#xff0c;你可以根据自己的实际情况进行调整&#xff1a; 个人信息 姓名联系方式&a…

图文深入介绍oracle资源管理(续)

1. 引言&#xff1a; 本文将承接上篇继续深入介绍oracle资源管理。本文重点介绍如何使用oracle资源管理器管理好DB。 2. 资源管理器&#xff1a; 可以使用图形界面 OEM$或命令行调用 DBMS RESOURCE MANAGER 程序包的过程进行数据库资源管理。 调用资源管理器的先决条件&…

瑞数后缀加密怎么处理

前言&#xff1a; 瑞数我们经常补环境通过&#xff0c;但是遇到瑞数后缀不知道怎么处理 就拿瑞数4来讲 解决方法&#xff1a; &#xff08;1&#xff09;传明文加密参数 一般情况&#xff0c;我们传明文加密参数也能访问 &#xff08;2&#xff09;再补环境基础调用open …

基于stm32的4G模块点灯实验

led模块功能封装 #include "led.h" #include "sys.h"//初始化GPIO函数 void led_init(void) {GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOB_CLK_ENABLE();//调用GPIO初始化函数gpio_initstruct.Pin GPIO_PIN_8 | GPIO_PIN_9;gpio_inits…

排序算法 —— 直接插入排序

目录 1.直接插入排序的思想 2.直接插入排序的实现 实现分析 实现代码 3.直接插入排序的分析 时间复杂度分析 空间复杂度分析 稳定性 1.直接插入排序的思想 直接插入排序的思想就是把待排序的元素按其关键码值的大小依次插入到一个已经排好序的有序序列中&#xff0c…

pycharm调试带参数命令行的程序

点击 run configuration 点击加号&#xff0c;选择python name填写程序名字&#xff0c;script填写程序路径&#xff0c;下一行填写传入的参数 点击apply&#xff0c;再点ok&#xff0c;即可debug 参考&#xff1a; pycharm 调试模式下命令行参数的传递_pycharm debug传参 ya…

项目实战:构建 effet.js 人脸识别交互系统的实战之路

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀构建 effet.js &#x1f4d2;1. 什么是effet.js&#x1f4dc;2. 为什么需要使用effet.js&#x1f4dd;3. effet.js的功能&#x1f4da;4. 使用…