数据结构--二叉树的顺序结构及实现

2023-11-17

前言

普通的二叉树是不适合用数组来存储的,因为这样会存在大量的空间浪费,但是完全二叉树却更适合用顺序结构存储。

堆的概念以及结构

堆的概念
堆是一种二叉树,可以用顺序结构的数组来存储。(这里的堆和操作系统的虚拟进程地址空间的堆不一样,一个是数据结构一个是操作系统的管理内存的一块区域分段)
把所有元素按完全二叉树的顺序存储方式存储在一个一维数组中。

堆的性质
1.堆的某个节点的值总是不大于或者不小于其父节点的值;
2.堆是一棵完全二叉树;

根节点最大的堆叫做最大堆或者大根堆,根节点最小的堆叫做最小堆或者小根堆。

堆的结构
在这里插入图片描述

堆的搭建

1.向上调整创建堆


void AdjustUp(HPdatatype *a,int child)//小堆
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] > a[child])
			{
				Swap(&a[parent], &a[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
		else
		{
			break;
		}
	}
}
void Heapbuild1(int *a,int n)//直接就是对a数组进行建堆打印
{								//向上建堆
	for (int i = 1;i < n;i++)//一开始让第0个成为堆
	{
		AdjustUp(a, i);//每给一个下标数,就相当于后接的数,依次向上调整成堆
	}//建堆
}

2.向下调整建堆

向下调整有个前提:左右子树必须是一个堆

void AdjustDown(HPdatatype* a,int parent,int n)//小堆
{
	HPdatatype minchild = parent * 2 + 1;
	while (minchild < n)//最小孩子没超出范围时
	{
		if (minchild+1<n&&(a[minchild + 1] < a[minchild]))
			{
				minchild++;
			}
		if (a[parent] > a[minchild])
		{
			Swap(&a[minchild], &a[parent]);
			parent = minchild;
			minchild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void Heapbuild2(int* a, int n)//向下调整建堆
{
	for (int i = (n - 1 - 1) / 2;i >= 0;i--)//从最后一个节点的父亲开始,再去减减 调整
	{
		AdjustDown(a, i, n);
	}
}

注意:,一开始要从最后一个节点的父亲开始,以它为父节点,左右子树满足堆,然后向下调整,让这个小子树为堆,最后对当前的parent减减,在去不断让子树成堆,parent不断向上,最后整体也就建成了堆。

比较两种建堆的时间复杂度
1.向上调整建堆
时间复杂度 O(N*logN)
节点越多,调整越多。
有N个数,每次向上调整时间复杂度为 O(logN)(大概为2k=N)
2.向下调整建堆
时间复杂读 O(N)
节点越多,调整的越少
(最后一层节点数量2k-1(假设是满的),都快等于总节点数量了2k-1几乎一半了,然而向下调整建堆却不从最后一层开始)
证明:
在这里插入图片描述
利用错位相减得,时间复杂度为O(N).
综上,建堆最好用向下建堆法!

堆的实现

头文件

typedef int HPdatatype;
typedef struct
{
	HPdatatype* a;
	int size;
	int capacity;

}Heap;

void HeapInit(Heap*hp);
void HeapDestory(Heap* hp);
void HeapPush(Heap* hp, HPdatatype x);
void HeapPop(Heap* hp);
HPdatatype HeapTop(Heap* hp);
int HeapSize(Heap* hp);
bool HeapEmpty(Heap* hp);
void AdjustUp(HPdatatype* a, int child);
void AdjustDown(HPdatatype* a, int parent, int n);
void Swap(HPdatatype* a, HPdatatype* b);
void HeapPrint(Heap* hp);

函数定义

初始化
与顺序表的类似

void HeapInit(Heap* hp)//初始化
{
	assert(hp);
	hp->a = NULL;
	hp->capacity = hp->size = 0;
}

销毁

void HeapDestory(Heap* hp)//销毁
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->capacity = hp->size = 0;
}

插入数据
与顺序表一样,先判断是否要扩容,然后把插入的数据放在数组的最后,再去向上调整

void AdjustUp(HPdatatype *a,int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] > a[child])
			{
				Swap(&a[parent], &a[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
		else
		{
			break;
		}
	}
}
void Swap(HPdatatype* a, HPdatatype* b)
{
	HPdatatype c = 0;
	c = *a;
	*a = *b;
	*b = c;
}
void HeapPush(Heap* hp, HPdatatype x)//插入数据
{
	assert(hp);
	if (hp->capacity == hp->size)
	{
		int newCapacity = hp->capacity == 0 ? 4 :hp-> capacity * 2;
		HPdatatype* new = (HPdatatype*)realloc(hp->a, sizeof(HPdatatype) * newCapacity);
		if (new == NULL)
		{
			perror("realloc fail:");
			exit(-1);
		}
		hp->a = new;
		hp->capacity = newCapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;
	AdjustUp(hp->a, hp->size-1);
}

删除数据
在顺序表里,删除就是size–;但是在堆里,删除的是堆顶的元素
这里的算法是,1.先将堆顶元素和最后一个元素互转2.然后size–3.最后把堆顶那个数向下调整

void AdjustDown(HPdatatype* a,int parent,int n)
{
	HPdatatype minchild = parent * 2 + 1;
	while (minchild < n)//最小孩子没超出范围时
	{
		if (minchild+1<n&&(a[minchild + 1] < a[minchild]))
			{
				minchild++;
			}
		if (a[parent] > a[minchild])
		{
			Swap(&a[minchild], &a[parent]);
			parent = minchild;
			minchild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapPop(Heap* hp)// 删除堆顶元素
{
	assert(hp);
	assert(!HeapEmpty(hp));
	Swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	AdjustDown(hp->a,0,hp->size);
}

返回堆顶元素

HPdatatype HeapTop(Heap* hp)//返回堆顶元素
{
	assert(hp);
	assert(!HeapEmpty(hp));
	return hp->a[0];
}

返回堆的数量

int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->size - 1;
}

判断堆是否为空

bool HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->size == 0;
}

打印

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

主函数

int main()
{
	Heap hp;
	HeapInit(&hp);//创建结构体变量再去初始化,把数组a的值一个个传过去malloc建堆
	int a[] = { 18,15,17,56,28,1,65,38,27,37 };
	for (int i = 0;i < sizeof(a) / sizeof(int);i++)
	{
		HeapPush(&hp, a[i]);
	}
	HeapPrint(&hp);
	HeapPush(&hp, 10);
	HeapPrint(&hp);
	HeapPop(&hp);
	HeapPrint(&hp);
	//打印也可以
	while (!HeapEmpty(&hp))
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
	return 0;
}

但是调用print函数和不断top返回数据,打印出来是不一样的。
不断的top 会不断返回完后再去调整成新堆,顺序和之间print的不一样。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构--二叉树的顺序结构及实现 的相关文章

  • visual studio中配置OpenCVsharp

    只能在线下载 每次新建项目就要下载一次 没找到离线下载的方式 很可恶 visual studio2019 C 语言 配置OpenCVsharp当前最新版 4 6 0 在浏览界面搜索OpenCVsharp 下载OpenCVsharp4和对应r
  • unity 路径

    IOS Application dataPath Application xxxxxxxx xxxx xxxx xxxx xxxxxxxxxxxx xxx app Data Application streamingAssetsPath A
  • 使用 .net + blazor 做一个 kubernetes 开源文件系统

    背景 据我所知 目前 kubernetes 本身或者其它第三方社区都没提供 kubernetes 的文件系统 也就是说要从 kubernetes 的容器中下载或上传文件 需要先进入容器查看目录结构 然后再通过 kubectl cp 指令把文

随机推荐

  • linux系统 在python3.6/CUDA 11环境下安装tensorflow 1.15

    今天在实验室服务器 3090 上跑别人用tensorflow写的代码 CPU使用率飙高 吓得我赶紧停了QAQ 后来发现是因为GPU无法使用 其原因是官网中cuda11 X 仅支持tf2 X 不支持tf1 X 通过查阅资料 参考大佬的方法 最
  • 力扣-912题 排序数组(C++)- 快排必须烂熟于心

    题目链接 https leetcode cn com problems sort an array 题目如下 class Solution public vector
  • vue3中百度地图的使用

    在vue3中使用百度地图 vue3 百度地图 文章目录 在vue3中使用百度地图 前言 一 百度地图在vue3中的引入 二 页面内容 注意事项 三 异步加载文件 四 图 前言 具体为百度地图引入 如何使用点位和自定义点位信息窗口 提示 以下
  • 2015蓝桥杯——密文搜索

    题目描述 标题 密文搜索 福尔摩斯从X星收到一份资料 全部是小写字母组成 他的助手提供了另一份资料 许多长度为8的密码列表 福尔摩斯发现 这些密码是被打乱后隐藏在先前那份资料中的 请你编写一个程序 从第一份资料中搜索可能隐藏密码的位置 要考
  • Dockerfile命令集

    Dockerfile与docker build命令 1 什么是Dockerfile 1 1 Dockerfile 1 2 docker build命令 1 3 Dockerfile相关指令描述 2 Dockerfile命令详情 2 1 FR
  • Java基础 —— 异常

    目录 异常的概念及分类 异常的处理try catch finally 异常抛出throw s 自定义异常 异常的概念及分类 什么是异常 异常是指在程序的运行过程中发生的一些不正常事件 比如 除0溢出 数组下标越界 所要读取的文件不存在 异常
  • 详解基于罗德里格斯(Rodrigues)公式由旋转向量到旋转矩阵的 Python 实现

    文章目录 旋转向量 rotation vector 旋转矩阵 rotation matrix 罗德里格斯公式 Rodrigues formula 基于 Python 和 NumPy 实现 Rodrigues 公式 旋转向量 rotation
  • js __proto__、prototype 、constructor 三者关系总结

    一 proto 属性 proto 怎么读 杠杠 proto 杠杠 proto 读作 dunder proto double underscore proto 的缩写 并且它前后两边 分别是 两个 下划线 由 proto 属性来连接对象 直到
  • 服了呀,被现在的00后卷麻了....

    现在的小年轻真的卷得过分了 前段时间我们公司来了个00年的 工作没两年 跳槽到我们公司起薪18K 都快接近我了 后来才知道人家是个卷王 从早干到晚就差搬张床到工位睡觉了 最近和他聊了一次天 原来这位小老弟家里条件不太好 一大家子指望他一个人
  • 以太猫白皮书

    转载自 https ethfans org posts cryptokitties whitepapaer 以太猫 一款由区块链技术赋予其收藏价值和繁育能力的猫咪 摘要 随着区块链技术持续占据各大新闻头条 加密货币以其富有争议的价值和对金融
  • C关键字volatile总结;*((volatile unsigned int *)0X020C4068)什么含义?

    1 volatile 关键字是一种类型修饰符 用它声明的类型变量表示可以被某些编译器未知的因素更改 volatile 提醒编译器它后面所定义的变量随时都有可能改变 因此编译后的程序每次需要存储或读取这个变量的时候 都会直接从变量地址中读取数
  • 双眼融合训练一个月_视觉融合功能的四种训练方法

    最起码要训练一个月以上 一 视觉融合游戏 一支铅笔 化实为虚 化虚为实 减少双眼视差 动作 1 取一支笔 放在距脸部45厘米的位置 再选远处一个参照物 2 先聚焦看铅笔 这时远处参照物看起来有两个影子 3 然后聚焦参照物 这时会看到两支铅笔
  • C++:类和对象:对象的初始化和清理

    1 前言 构造和析构的背景 1 C 中的面向对象来源于生活 每个对象都会有初始值以及对象销毁前的清理数据设置 2 对象的初始化和清理是两个非常重要的安全问题 一个对象或者变量没有初始状态 对其使用后果是未知的 同样一个对象或者变量没及时清理
  • Linux系统查看CPU个数&超线程&线程数

    Linux系统查看CPU个数 超线程 线程数 cpu 核心数与线程数 Linux系统查看CPU个数 超线程 线程数 小命令 Linux查看CPU详细信息 简书 jianshu com Intel CPU产品规范 英特尔 产品 处理器 英特尔
  • win10用html文件做壁纸,利用win10自带工具制作动态壁纸的简单方法

    微软在最强大操作系统利用win10自带工具制作动态壁纸的简单方法的详细介绍 利用win10自带工具制作动态壁纸的简单方法 把图片做成动态壁纸 总共分4步 1 准备素材 2 素材导入 3 调整效果 4 导出视频 接下来 笔者就为分步讲解以下制
  • VMware安装CentOS7

    目录 一 安装CentOS 二 网络配置 三 虚拟机克隆 一 安装CentOS 1 进入VMware主页面 选择 创建新的虚拟机 2 进入新建虚拟机向导 配置类型选择 典型 3 浏览 选择下载好的iso文件 CentOS7驱动下载地址 Ce
  • vue-cli3.0+antd+select

    前言 通过和 iview 和 element antd 还是有他特殊的优势的 那就是功能更加丰富 当然 功能丰富同样代表着复杂程度相比较来说 更高了 这里来对他进行二次封装 此外 在我们实际应用的情况下会遇到给他赋值赋不上 用default
  • java中的类

    4 1 类是什么 1 类 类型 数据类型 复合数据类型 自定义复合数据类型 为什么有复合数据类型 基本数据类型就8种 在开发中远远不够 所以我们就需要一个可以根据自己的需求随时能制作出一个自己需要的数据类型 2 类 具有共同性质的一组事物的
  • sawyer机械臂环境搭建

    sawyer机械臂环境搭建 1 ros安装 2 sawyer sdk安装 3 sawyer sdk环境配置 4 测试 1 ros安装 ros的安装方法参考创客智造 注意安装ubuntu系统对应版本的ros 新版ros melodic可以在w
  • 数据结构--二叉树的顺序结构及实现

    文章目录 前言 堆 堆的概念以及结构 堆的搭建 堆的实现 前言 普通的二叉树是不适合用数组来存储的 因为这样会存在大量的空间浪费 但是完全二叉树却更适合用顺序结构存储 堆 堆的概念以及结构 堆的概念 堆是一种二叉树 可以用顺序结构的数组来存