树结构的讲解与二叉树的基本运用

news2024/11/24 20:31:30

目录:

 一,树的基本知识

二,树的类型

三,树的存储

四,树的基本运算

五,二叉树堆的基本运用


 一,树的基本知识

        树是一种非线性的数据结构,它是由n个有限结点组合而成为一个具有层次关系的集合,把它叫做树因为它看起来像一颗倒挂的树,也就是说它的根是朝上,叶朝下的,如下图:


        在以上的所有结点种,其中,A结点称为根结点,而根节点没有前驱结点,其它结点都是通过层次非线性进行连接,它们彼此间不相交(注意此特点,要是相交的话就不是树)。

树结构中有以下常用的关健名:

结点的度:一个结点含有的子树个数称为该结点的度,如上图:A有子树B,C,D,度为3。

叶子结点或终端结点:度为0的结点称为叶结点,如上图中的J,F,K,L,H,I。

双亲结点:若一个结点含有子结点,则这个结点称为其子结点的双亲结点,如上图中A是B的双亲结点。

孩子结点:一个结点含有的子树的根结点称为该结点的子结点,如上图:B是A的孩子结点。

兄弟结点:具有相同父结点的结点互为兄弟结点,如上图:B,C,D是兄弟结点。

树的度:一棵树中,最大的结点的度称为树的度,如上图:树的度为3。

结点的层次:从根开始定义起,根为第一层,根的结点为第二层,其余的以此类推。

树的高度或深度:树中结点的最大层次,如上图:树的高度为4。

树的祖先:一颗树的根节点称为所有子结点的祖先,如上图:A为祖先。

子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙。

森林:由多棵不相交的树的集合称为森林。


二,树的类型

        树的类型根据结构来讲有多种,我们最常用的类型就是二叉树,在初步学习中先深入学习二叉树即可。

1,二叉树的概念

二叉树具有以下特征:

       二叉树的结构中不存在度大于2的结点,即在二叉树中,最多由两个结点。

       二叉树的子树有左右之分,次序不可颠倒,因为二叉树是有序结构。

2,特殊的二叉树

       满二叉树:假设一颗二叉树的高度为h,每一层的结点都是满的,即每个结点的度都为2。

       完全二叉树:假设一颗二叉树的高度是h,前h-1层都是满的,最后一层不一定满,且从左到右都连续。

        


三,树的存储

       首先,我们要明白一点,在二叉树中,最多就有两个结点,并且这两个结点还都是有序的,在设计中,可能用到增添查找,用数组的结构存储在运用中比较麻烦,因此,二叉树最好用链表的形式来存储,而普通的树结构中结点并不确定,要用链式结构很是麻烦,普通的树结构最好用数组的形式进行存储。具体定义如下:

1,二叉树的形式

struct TreeNode
{
    //在这里树中存储整型数据,也可换成其它类型
    int val;
    //左子树,不存在通常置为NULL
    struct TreeNode* left;
    //右子树,不存在通常置为NULL
    struct TreeNode* right;
};

2,普通树的形式

//说明树的度
#define Max = 10;
struct TreeNode
{
    //存放整型数据
    int val;
    //顺序表存放孩子指针
    struct TreeNode* child;
};

        树的设计方式其实还有很多种,但以上两种方式是我们目前最为基础的使用,在后面深入学习中还会设计更为精妙的存储方式,在我们计算机上的文件系统基本就是通过精妙建立的树状结构来存储,只是这种结构比较复杂,在后面的文章会给大家进行介绍。


四,树的基本运算

        在了解以上的基本知识点后我们必须学会对树进行基本的运算,而笔者对树的研究总结了以下几点重要公式和定理,在树的计算中可很方便的运用。

1,若规定根结点的层数为1,则一颗非空二叉树的第i层上最多有2^(i-1)个结点。

2,若规定根结点的层数为1,则深度为h的二叉树的最大结点数是2^(h) - 1。

3,对任何一颗二叉树,如果度为0,其叶结点(即度为0)个数为n0,度为2的分支结点个数为n2,则有n0 = n2 + 1。(此公式为重点公式)

4,若规定根结点的层数为1,具有n个结点的满二叉树的深度h = log2(n + 1)。

5,一颗完全二叉树度为1的结点数要么为1,要么为0,因为其有序结构是从左向右排布的。

        在以上公式中,第三个和第五个为重点记忆,运用较多,第一个和第二个较为简单,可自行推理,第四个运用的比较少。

        明白以上的逻辑后我们需注意在树计算中的几点隐含条件,接下来我用例题的形式跟大家进行解说。        

        在树的运算中要注意的是n1的情况(以上的情况只限制与在完全二叉树中),其它的运算掌握以上的公式和定理,并且自行灵活运用都比较简单。


五,二叉树堆的基本运用

堆的概念:

        首先我们先了解下堆的概念,对于这个名词我们很是熟悉,在计算机的存储方式中就有堆的存储,但这里的堆并不是存储方式的那个堆,而是一种非线性结构中的完全二叉树,通常我们会将二叉树改制为堆结构或直接将数据改成堆结构,而在改制之前,我们先了解以下知识。

堆的分类:

        1,小堆:树中任意一个父亲都小于或等于孩子。

        2,大堆:树中任意一个父亲都大于或等于孩子。

完全二叉树父子结点的关系:

        如若我们用顺序表的形式进行存储二叉结构(下标从0开始),在完全二叉树中我们观察不同两层直接双亲结点和孩子结点之间的数学联系,不难发现,其中有:如图中关系:


        堆结构的最大问题就在于排序的时候其中的逻辑连接,明白了以上知识后,我们先要思考,如何用孩子结点找双亲结点之间的关系来进行算法的设置,总的来说就是通过双亲与孩子的比较,进而进行大堆或小堆的建立,其中,最为主要的算法为向下调整或向下调整,代码如下:

//数据从下往上调整
void AdjustUp(int* a, int child)//child是孩子,a是顺序表
{
    int parent = (child - 1) / 2;
    while (child > 0)
    {
        //大堆树状规则排序法
        if (a[child] > a[parent])
        {
            //交换
            int t = a[child];
            a[child] = a[parent];
            a[parent] = t;
            //继续下一次往上遍历
            child = parent;
            parent = (parent - 1) / 2;
        }
        //因为一开始就是不断向上面排序,树状序列都已经排序好了,当满足排序要求时说明都已经满足了
        else
        {
            break;
        }
    }
}
//数据从上往下调整
void AdjustDown(int* a, int n, int parent)//a是顺序表,n是大小,parent是双亲
{
    int child = parent * 2 + 1;
    while (child < n)
    {
        //大堆树状规则排序法
        // 找出小的那个孩子,在这里要注意的是控制下一个孩子结点的不能超出范围

        if (child + 1 < n && a[child + 1] > a[child])
        {
            ++child;
        }
        if (a[child] > a[parent])
        {
            //交换
            int t = a[child];
            a[child] = a[parent];
            a[parent] = t;
            // 继续往下调整
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

        因为堆是完全二叉树,在排布的时候我们只能通过双亲与孩子的关系来进行排序分布,即上下结构调整,也就是以上的算法排布。

        在队中还可自行运用很多种算法设置,但基本算法都要靠向上调整算法或向下调整算法。在这里我先用一种方法演示:

首先:我们设置一个自己的头文件Heap.h:

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

typedef struct Heap
{
    //以顺序表的形式存储堆
    int* a;
    int size;
    int capacity;
}HP;

void AdjustUp(int* a, int child);
void AdjustDown(int* a, int n, int parent);
void HeapPrint(HP* php);
void HeapInit(HP* php);
void HeapInitArray(HP* php, int* a, int n);
void HeapDestory(HP* php);

具体代码如下:

#include "Heap.h"
//数据从下往上调整
void AdjustUp(int* a, int child)//child是孩子,a是顺序表
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		//大堆树状规则排序法
		if (a[child] > a[parent])
		{
			//交换
			int t = a[child];
			a[child] = a[parent];
			a[parent] = t;
			//继续下一次往上遍历
			child = parent;
			parent = (parent - 1) / 2;
		}
		//因为一开始就是不断向上面排序,树状序列都已经排序好了,当满足排序要求时说明都已经满足了
		else
		{
			break;
		}
	}
}
//数据从上往下调整
void AdjustDown(int* a, int n, int parent)//a是顺序表,n是大小,parent是双亲
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//大堆树状规则排序法
		// 找出小的那个孩子,在这里要注意的是控制下一个孩子结点的不能超出范围
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;
		}
		if (a[child] > a[parent])
		{
			//交换
			int t = a[child];
			a[child] = a[parent];
			a[parent] = t;
			// 继续往下调整
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//输出堆
void HeapPrint(HP* php)
{
	assert(php);
	fprintf(stdout, "堆的数据: ");
	for (int i = 0; i < php->size; i++) {
		fprintf(stdout, "%d ", php->a[i]);
	}
	puts("");
}
//堆的初始化
void HeapInit(HP* php)
{
	assert(php);
	php->a = 0;
	php->size = php->capacity = 0;
}
//堆的构建
void HeapInitArray(HP* php, int* a, int n)
{
	assert(php && a);
	php->a = (int*)malloc(sizeof(int) * n);
	if (!php->a)
	{
		perror("php->a malloc");
		exit(-1);
	}
	php->capacity = n;
	memcpy(php->a, a, sizeof(int) * n);
	php->size = n;
	//建立堆,即进行排序,在这里以向上调整算法进行树状排布
	for (int i = 1; i < php->size; i++)
	{
		AdjustUp(php->a, i);
	}
}
//删除,因为是动态管理,要自己删除
void HeapDestory(HP* php)
{
	assert(php);
	free(php);
	php = 0;
}
//大堆的构造
int main()
{
	int a[] = { 2,3,5,7,4,6,8 };
	HP* p = (HP*)malloc(sizeof(HP));//建立堆结构p,在这里以顺序表结构进行存储
	HeapInit(p);//初始化
	HeapInitArray(p, a, sizeof(a) / sizeof(int));//进行堆的构造
	HeapPrint(p);//输出堆
	HeapDestory(p);
	return 0;
}

        总:从树之后相关的结构和算法就要开始上点难度了,对于要想学好这一块,自己必须要多多试试上机练习并自己学会改动其中的算法设置,以上的设置并不算完整,算法还有大有改进的空间,我们自己要学会自行设置并且找到其中算法关键效应,然后自己设置更加高效的算法,这样才能达到举一反三的效果。

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

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

相关文章

【1++的Linux】之进程(三)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的Linux】 文章目录 一&#xff0c;什么是进程地址空间&#xff1f;二&#xff0c;进程地址空间是怎么设计的&#xff1f;三&#xff0c;为什么要有进程地址空间&#xff1f; 一&#xff0c;什…

【C++杂货铺】一颗具有搜索功能的二叉树

文章目录 一、二叉搜索树概念二、二叉搜索树的操作2.1 二叉搜索树的查找2.2 二叉搜索树的插入2.3 二叉搜索树的删除 三、二叉搜索树的实现3.1 BinarySearchTreeNode&#xff08;结点类&#xff09;3.2 BinarySearchTree&#xff08;二叉搜索树类&#xff09;3.2.1 框架3.2.2 in…

【力扣485】最大连续 1 的个数

&#x1f451;专栏内容&#xff1a;力扣刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、题目描述二、题目分析1、最值模拟2、双指针 一、题目描述 题目链接&#xff1a;最大连续 1 的个数 给定一个二进制数…

辨析常见的医学数据分析(相关性分析回归分析)

目录 1 常见的三种分类结果&#xff1f; 2 什么是相关性分析&#xff1f; 相关性分析的结果怎么看&#xff1f; 3 什么是回归分析&#xff1f; 1&#xff09;前提 2&#xff09;常见的回归模型 4 对于存在对照组实验的医学病例如何分析&#xff1f; 1&#xff09;卡方检验…

万字解析30张图带你领略glibc内存管理精髓

最近在逛知乎的时候&#xff0c;看到篇帖子&#xff0c;如下&#xff1a; 看了下面所有的回答&#xff0c;要么是没有回答到点上&#xff0c;要么是回答不够深入&#xff0c;所以&#xff0c;借助本文&#xff0c;深入讲解C/C内存管理。 1 写在前面 源码分析本身就很枯燥乏味…

服务注册发现_解读Eureka注册中心UI界面

参数&#xff1a; Environment: 环境&#xff0c;默认为test&#xff0c;该参数在实际使用过程中&#xff0c;可以不用更改Data center&#xff1a; 数据中心&#xff0c;使用的是默认的是 “MyOwn”Current time&#xff1a;当前的系统时间Uptime&#xff1a;已经运行了多少时…

JavaScript系列从入门到精通系列第六篇:JavaScrip当中的运算符,主要涉及JavaScript当中的六大数据类型的四则运算

文章目录 前言 一&#xff1a;算数运算符 1&#xff1a;Number类型的四则运算 2&#xff1a;其他数据类型的四则运算 (一)&#xff1a;加法运算 (二)&#xff1a;减法运算 3&#xff1a;乘法运算 4&#xff1a;除法运算 5&#xff1a;取模运算 前言 运算符也叫操作符。…

极大似然函数和似然函数的区别

极大似然函数和似然函数 "极大似然函数"和"似然函数"是统计学和机器学习中常见的两个概念&#xff0c;它们之间的区别在于它们在不同上下文中的使用方式&#xff1a; 似然函数&#xff08;Likelihood Function&#xff09;&#xff1a; 似然函数通常表示为…

[pai-diffusion]pai的easynlp的diffusion模型训练

PAI-Diffusion模型来了&#xff01;阿里云机器学习团队带您徜徉中文艺术海洋 - 知乎作者&#xff1a;汪诚愚、段忠杰、朱祥茹、黄俊导读近年来&#xff0c;随着海量多模态数据在互联网的爆炸性增长和训练深度学习大模型的算力大幅提升&#xff0c;AI生成内容&#xff08;AI Gen…

基于微信小程序快递取件上门预约服务系统设计与实现(开题报告+任务书+源码+lw+ppt +部署文档+讲解)

文章目录 前言运行环境说明用户的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌…

电子电子架构——AUTOSAR信息安全机制有哪些(下)

电子电子架构——AUTOSAR信息安全机制有哪些&#xff08;下&#xff09; 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 人们会在生活中不断攻击你。他们的主要…

使用FastChat部署Baichuan2

1. 引言 近来&#xff0c;大型语言模型的市场需求呈现出蓬勃发展的态势。然而&#xff0c;仅仅掌握模型的数据准备和训练是不够的&#xff0c;模型的部署方法也变得至关重要。在这篇文章中&#xff0c;我们将以Baichuan2为例&#xff0c;利用FastChat进行模型部署的实战操作。…

IDEA 中 Maven 报错 Cannot resolve xxx【终于解决了】

问题 pom中已经添加相关依赖&#xff0c;maven刷新也没有用&#xff0c;依旧是疯狂报错 解决办法 不断的查询资料&#xff0c;总结一下试过的办法。 解决办法一&#xff1a;清除缓存 File -> Invalidate Caches/Restart -> Invalidate And Restart 试了之后也就报错 …

C# EPPlus 访问 Excel表格

EPPlus是什么&#xff1f; 一个访问Excel表格的库&#xff0c;调用相当简单 怎么访问&#xff1f; 表格可以简单理解成一个二维数组我希望访问表格像二维数组一样简单我希望消耗不算太大 封装一个类 下载DLL以及这个文件&#xff1a;《下载传送门->》 注意需要导入EP…

uniapp iOS离线打包——上传到App Store

uniapp iOS离线打包&#xff0c;如何打包上传到App Store&#xff1f; 文章目录 uniapp iOS离线打包&#xff0c;如何打包上传到App Store&#xff1f;打包上传 App Store App iOS 离线打包 上一篇分享部分工程配置 打包上传 App Store 选中项目工程&#xff1a;点击 工具栏 P…

虚幻4学习笔记(14)界面切换、局域网联机

虚幻4学习笔记 创建游戏加入游戏搜索服务器加入服务器刷新服务器 B站UP谌嘉诚课程&#xff1a;https://www.bilibili.com/video/BV164411Y732 创建游戏 新建三个UI界面 FindServer、JoinServer、MainMenu 打开MainMenu 打开FindServer 添加Scroll Box滚动框 添加Circular T…

【计算机网络】——应用层

// 图片取自王道 仅做交流学习 一、基本概念 应用层概述 协议是 网络层次模型 中多台主机之间 同层之间进行通信的规则。是一个水平概念 垂直空间上&#xff0c;向下屏蔽下层细节&#xff0c;向上提供服务接入&#xff0c;多台主机之间同层之间形成一条逻辑信道。 应用层的…

关于Pandas数据分析

pandas的数据加载与预处理 数据清洗&#xff1a;洗掉脏数据 整理分析&#xff1a;字不如表 数据展现&#xff1a;表不如图 环境搭建 pythonjupyter anaconda Jupyter Notebook Jupyter Notebook可以在网页页面中直接编写代码和运行代码, 代码的运行结果也会直接在代码块下显示…

zabbix学习2--zabbix6.x高可用

文章目录 1. server高可用-默认HA2. 访问高可用 1. server高可用-默认HA 1.部署zabbix单节点后&#xff0c;配置添加HANodeName和NodeAddress即为HA架构 2.zabbix1故障后切换zabbix2使用 3.浏览器访问主机1&#xff0c;使用主机1php前端连接mysql后zabbix2提供后台服务--------…

Linux:进程的本质和fork初识

文章目录 回顾进程查看进程的方式fork 回顾进程 前面对进程进行了一些初步的认知&#xff0c;比如进程可执行程序内核数据结构PCB&#xff0c;再比如可以通过ps命令搭配管道寻找进程&#xff0c;通过/proc系统文件夹查看进程的信息&#xff0c;这些都是前面对进程建立起来的一…