【数据结构初阶】二叉树(2)

news2025/2/5 17:59:56

二叉树顺序结构

    • 1.二叉树的顺序结构及实现
      • 1.1二叉树的顺序结构
    • 1.2 堆的概念及结构
      • 1.3 堆的实现
      • 1.3.1向上调整
      • 1.3.2向下调整
      • 1.3.3交换函数
      • 1.3.4打印
      • 1.3.5初始化
      • 1.3.6销毁
      • 1.3.7插入
      • 1.3.8删除
      • 1.3.9获得堆顶元素
      • 1.3.10判断是否为空
      • 1.3.6 堆的代码实现
    • 1.3.2堆的创建
    • 1.3.3 建堆时间复杂度
    • 1.4 堆的应用
      • 1.4.1 堆排序

1.二叉树的顺序结构及实现

1.1二叉树的顺序结构

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

在这里插入图片描述

1.2 堆的概念及结构

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

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。
    在这里插入图片描述
    练习题:
    1.下列关键字序列为堆的是:()
    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 之后需重建堆,在此过程中,关键字之间的比较次
    数是()。
    A 1
    B 2
    C 3
    D 4
    3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为
    A(11 5 7 2 3 17)
    B(11 5 7 2 17 3)
    C(17 11 7 2 3 5)
    D(17 11 7 5 3 2)
    E(17 7 11 3 5 2)
    F(17 7 11 3 2 5)
    4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是()
    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]

练习题答案
1.A 2.C 3.C 4.C

1.3 堆的实现

1.3.1向上调整

在这里插入图片描述

向上调整前提:前面数据是堆

void AdjustUp(HPDateType* 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;
		}
	}
}

1.3.2向下调整

int array[] = {27,15,19,18,28,34,65,49,25,37};

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

在这里插入图片描述

void AdjustDown(HPDateType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child > 0)
	{
		if ((child + 1 > n) && a[child + 1] > a[child])
		{
			child++;
		}
		if (a[parent] > a[child])
		{
			swap(&a[parent], &a[child]);
			child = parent;
			parent = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

1.3.3交换函数

void swap(HPDateType* p1, HPDateType* p2)
{
	HPDateType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

1.3.4打印

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

1.3.5初始化

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

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

1.3.6销毁

void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}

1.3.7插入

先插入一个10到数组的尾上,再进行向上调整算法直到满足堆。
在这里插入图片描述

void HeapPush(HP* php, HPDateType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDateType* tmp = (HPDateType*)realloc(php->a, sizeof(HPDateType) * newCapacity);
		php->capacity = newCapacity;
		php->a = tmp;
	}
	php->a[php->size] = x;
	php->size++;
	AdjustUp(php->a, php->size - 1);
}

1.3.8删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
在这里插入图片描述

void HeapPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	swap(&php->a[0], &php->a[php->size]);
	php->size--;
	AdjustDown(php->a, php->size, 0);
}

1.3.9获得堆顶元素

HPDateType HeadTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

1.3.10判断是否为空

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

1.3.6 堆的代码实现

Heap.h

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

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

//向上调整
void AdjustUp(HPDateType* a, int child);
//向下调整
void AdjustDown(HPDateType* a, int n, int parent);
//交换函数
void swap(HPDateType* p1, HPDateType* p2);
//打印
void HeapPrint(HP* php);
//初始化
void HeapInit(HP* php);
//销毁
void HeapDestroy(HP* php);
//插入
void HeapPush(HP* php, HPDateType x);
//删除
void HeapPop(HP* php);
//获得堆顶元素
HPDateType HeapTop(HP* php);
//判断是否为空
bool HeapEmpty(HP* php);

Heap.c

#include"Heap.h"

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

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

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 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 HeapPush(HP* php, HPDataType x)
{
	assert(php);
	// 扩容
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)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 HeapPrint(HP* php)
{
	assert(php);

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

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

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}

bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

test.c

int main()
{
	int a[] = { 65,100,70,32,50,60 };
	HP hp;
	HeapInit(&hp);
	for(int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		HeapPush(&hp, a[i]);
	}
	HeapPrint(&hp);
	
	return 0;
}

利用堆的代码,我们可以将数组排成有序,如下:

int main()
{
	int a[] = { 2,3,5,7,4,6,8,65,100,70,32,50,60 };
	HP hp;
	HeapInit(&hp);
	for (int i = 0; i < sizeof(a)/sizeof(int); i++)
	{
		HeapPush(&hp, a[i]);
	}
	HeapPrint(&hp);
	int k = 5;
	while (!HeapEmpty(&hp) && k--)
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}

	HeapDestroy(&hp);

	return 0;
}

1.3.2堆的创建

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

int a[] = {1,5,3,8,7,6};

在这里插入图片描述

1.3.3 建堆时间复杂度

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

1.4 堆的应用

1.4.1 堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:

  1. 建堆
    升序:建大堆
    降序:建小堆

建堆有两种方法:
(1)使用向上调整,插入数据的思想建堆。插入数据到新的数组,就是在不断插入的过程中向上调整实现排序

void Swap(HPDataType* pa, HPDataType* pb)
{
	HPDataType tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}
void AdjustUp(HPDataType* a, size_t child)
{
	size_t 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 HeapSort(int* a, int n)
{
	//升序,建大堆,向上
	size_t i = 0;
	for (i = 1; i < n; ++i)
	{
		AdjustUp(a, i);
	}
}

int main()
{
	int a[] = { 4, 3, 10 , 2, 5, 9 };
	HeapSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	return 0;
}

(2)使用向下调整,从倒数第一个非叶子节点开始,即最后一个节点的父亲,即[(size-1-1)/ 2 ]【找到这个父亲的节点,向下排序,然后这个父亲节点依次减一【就找到各个小堆,依次向下排序,就成为了一个堆。

void HeapSort(int* a, int n)
{
	//升序,建堆,向上
	/*int i = 0;
	for (i = 1; i < n; ++i)
	{
		AdjustUp(a, i);
	}*/
    //向下
	int i = 0;
	for (i = (n - 2) / 2; i >= 0; --i)
	{
		AdjustDown(a, i, n);
	}
}

💘不知不觉,【数据结构初阶】二叉树(2)以告一段落。通读全文的你肯定收获满满,让我们继续为数据结构学习共同奋进!!!

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

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

相关文章

迪文屏开发保姆级教程5—表盘时钟和文本RTC显示

这篇文章要讲啥事呢&#xff1f; 本篇文章主要介绍了在DGBUS平台上使用表盘时钟和文本时钟RTC显示功能的方法。 文哥悄悄话&#xff1a; 官方开发指南PDF&#xff1a;&#xff08;不方便下载的私聊我发给你&#xff09; https://download.csdn.net/download/qq_21370051/8864…

一文了解什么是微信小程序

如果您无需下载和管理即可获得像原生 iOS 或 Android APP 一样流畅的体验会怎样&#xff1f;腾讯通过微信小程序实现了这一替代方案。 一、什么是微信小程序&#xff1f;它们与原生应用程序和 H5 迷你网站相比如何 什么是微信小程序&#xff1f; 小程序是微信平台内构建的“…

Stage 模型

1. 基本概念 FA模型&#xff1a;HarmonyOS早期版本开始支持的模型&#xff0c;已经不再推荐。Stage模型&#xff1a;HarmonyOS 3.1版本开始新增的模型&#xff0c;目前主推并且会长期演进的模型。 Stage模型结构&#xff1a; 2. 应用及组件配置 2.1 工程级目录 AppScope 中…

Zookeeper应用场景有哪些?

ZooKeeper是⼀个典型的发布/订阅模式的分布式数据管理与协调框架&#xff0c;我们可以使⽤它来进⾏分布式数据的发布与订阅。另⼀⽅⾯&#xff0c;通过对ZooKeeper中丰富的数据节点类型进⾏交叉使⽤&#xff0c;配合Watcher事件通知机制&#xff0c;可以⾮常⽅便地构建⼀系列分…

Dockerfile构建镜像

Dockerfile构建镜像 Dockerfile 是一个文本格式的配置文件&#xff0c; 用户可以使用 Dockerfile 来快速创建自定义的镜像&#xff0c;另外&#xff0c;使 用Dockerfile去构建镜像好比使用pom去构建maven项目一样&#xff0c;有异曲同工之妙 基本结构 Dockerfile 由一行行…

【JavaWeb学习笔记】16 - JSon和Ajax

项目代码 https://github.com/yinhai1114/JavaWeb_LearningCode/tree/main/json https://github.com/yinhai1114/JavaWeb_LearningCode/tree/main/ajax 目录 〇、官方文档 一、JSon 1.JSon介绍 2.JSon快速入门 3.JSON对象和字符串对象转换 1.应用案例 2.注意事项和细节 …

【基础篇】二、字节码文件的组成 Arthas + jclasslib +javap

文章目录 1、jclasslib工具2、基础信息部分3、常量池部分4、方法部分&#xff08;从字节码指令看i&#xff09;5、三种1操作的性能对比6、javap -v命令7、jclasslib插件8、Arthas 1、jclasslib工具 字节码文件中保存的是源代码编译后的内容&#xff0c;以二进制方式存储&#…

面试题:Zabbix 和 Prometheus 到底怎么选?

文章目录 前言历史简介PrometheusZabbix 架构对比PrometheusZabbix 综合对比总结 前言 新公司要上监控&#xff0c;面试提到了 Prometheus 是公司需要的监控解决方案&#xff0c;我当然是选择跟风了。 之前主要做的是 Zabbix&#xff0c;既然公司需要 Prometheus&#xff0c;…

一个可以统计网页各个国家的浏览量的小工具

flag-counter 为网站添加【展示国家地区旗帜】FlagCounter访客统计 – 天祺围棋 很神奇 它是咋记录这个数据滴呢

el-date-picker中可能会遇到下拉选项框偏移

一般情况我们的下拉选项框偏移造成这种情况的就是我们同一个html页面中出现不同形式的下拉时间选择 当我们出现这种情况的时候只需要在我们这个html的日期时间选择器上加上不同的key值就可以了 这样就互不干扰

java.lang.EnumConstantNotPresentException

枚举常量不存在异常。当应用试图通过名称和枚举类型访问一个枚举对象&#xff0c;但该枚举对象并不包含常量时&#xff0c;抛出该异常 1.遇到的问题是我删除了带有CustomAnnotation注解的类,启动服务器还是会抛出异常 2.首先全局搜索了相关的枚举名字 确认没有引用。后发现项目…

使用netty做硬件测试代码的一般步骤

准备工作&#xff1a;搭建netty 1、util文件中准备好数据结构转换&#xff0c;byte[]数组转为int类型 大端写法&#xff0c;从高往低位读 public static int bytesToInt(byte[] bytes){int value;value ((bytes[0] & 0xff)<<24)|((bytes[1] & 0xff)<<16…

Plantuml之序列图语法介绍(十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

arcpy工具箱根据个别参数隐藏/显示其他参数

引入 工具箱属性中有验证参数&#xff08;如下图&#xff09;&#xff0c;该部分主要用于工具箱参数是否符合我们的要求&#xff0c;主要包含3方面的函数&#xff0c;分别是在打开工具箱调用的initializeParameters()、修改参数值后调用updateParameters()、为参数设置消息提示…

DevC++ easyx实现悬浮窗放入网格,与在函数分离过程中遇到的BUG下理解 函数的作用时域 以及 初始化与复位的关系。

这次就着上上上篇的悬浮窗代码DevC easyx实现图片拖动&#xff0c;一种悬浮窗实现原理与完整代码-CSDN博客 继续实现效果。 基本背景是搓出来图片拖动了&#xff0c;然后想把图片暂存到另一块。再细说背景的背景就是之前提到Unity复刻瓦片地图&#xff0c;想着整合一个铅笔绘…

web前端 JQuery下拉菜单的案例

浏览器运行结果&#xff1a; JQuery下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/17LXZigLQ8yau0toTGj4P_Q?pwd4332 提取码&#xff1a;4332 代码&#xff1a; <!doctype html> <html> <head> <meta charset"UTF-8"><…

Emu2:37亿参数开创多模态生成新篇章

引言 多模态任务在人工智能领域一直是极具挑战性的「技术高地」。智源研究院最近开源发布的新一代多模态基础模型Emu2&#xff0c;在这一领域取得了突破性进展。Emu2以其庞大的37亿参数规模和强大的多模态生成能力&#xff0c;为AI的多模态理解和生成开启了新的篇章。 模型概…

【重点!!!】【贪心】45.跳跃游戏II

题目 法1&#xff1a;贪心 贪心是最优解法&#xff0c;必须掌握&#xff01;重点理解&#xff0c;看B站视频辅助&#xff01;&#xff01;&#xff01; 在具体的实现中&#xff0c;我们维护当前能够到达的最大下标位置&#xff0c;记为边界。我们从左到右遍历数组&#xff0…

记账导出excel表格,用表格导出账目数据

我们每天都在跟金钱打交道,记账则是更好地管理自己财务的一种方式&#xff0c;传统记账不仅繁琐&#xff0c;还容易出错。那么&#xff0c;有没有简单、高效的记账方式呢&#xff1f;答案是肯定的&#xff01;今天&#xff0c;我们就向大家推荐一款全新的记账软件——晨曦记账本…

Windows系统重启Redis服务

Windows系统 在安装Redis的目录下打开终端 执行 redis-cli.exe shutdown先停止 Redis 服务 然后 执行 redis-server.exe启动Redis服务