树和二叉树(不用看课程)

1. 树

1.1 树的概念与结构

树是⼀种非线性的数据结构,它是由 n(n>=0) 个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,而叶朝下的。

• 有⼀个特殊的结点,称为根结点,根结点没有前驱结点。

• 除根结点外,其余结点被分成 M(M>0) 个互不相交的集合 T1、T2、……、Tm ,其中每⼀个集合 Ti(1 <= i <= m) 又是⼀棵结构与树类似的子树。每棵子树的根结点有且只有⼀个前驱,可以有 0 个或多个后继。因此,树是递归定义的。

 树形结构中,子树之间不能有交集,否则就不是树形结构。

非树形结构:

•  子树是不相交的(如果存在相交就是图了)(除了根节点之外,有其它的集合,这些集合就是树)
除了根结点外,每个结点有且仅有一个父结点
⼀棵N个结点的树有N-1条边

1.2树相关术语

 

父结点/双亲结点:若⼀个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点。
子结点/孩子结点:⼀个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点。
结点的度:⼀个结点有几个孩子,他的度就是多少;比如A的度为6,F的度为2,K的度为0。
树的度:⼀棵树中,最大的结点的度称为树的度; 如上图:树的度为 6。
叶子结点/终端结点:度为 0 的结点称为叶结点; 如上图: B C H I... 等结点为叶结点。
分支结点/非终端结点:度不为 0 的结点; 如上图: D E F G... 等结点为分支结点。
兄弟结点:具有相同父结点的结点互称为兄弟结点(亲兄弟); 如上图: B C 、D、E、F等 是兄弟结点。(H、I是表兄弟节点)。
结点的层次:从根开始定义起,根为第 1 层,根的子结点为第 2 层,以此类推。
树的高度或深度:树中结点的最大层次; 如上图:树的高度为 4。
结点的祖先:从根到该结点所经分支上的所有结点;如上图: A 是所有结点的祖先。比如P的祖先节点是(A、E、J)。
路径:⼀条从树中任意节点出发,沿父节点——子节点连接,达到任意节点的序列;比如A到Q的路径为: A-E-J-Q;H到Q的路径H-D-A-E-J-Q。
子孙:以某结点为根的子树中任⼀结点都称为该结点的子孙。如上图:所有结点都是A的子孙。
森林: m (  m>0  )棵互不相交的树的集合称为森林。一棵树也可以称为森林。

1.3 树的表示

孩子兄弟表示法:
树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既然保存值域,也要保存结点和结点之间的关系,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法。

1.4 树形结构实际运用场景

文件系统是计算机存储和管理文件的⼀种方式,它利用树形结构来组织和管理文件和文件夹。在文件 系统中,树结构被广泛应用,它通过父结点和子结点之间的关系来表示不同层级的文件和文件夹之间的关联。

2. 二叉树

2.1 概念与结构

在树形结构中,我们最常用的就是⼆叉树,⼀棵⼆叉树是结点的⼀个有限集合,该集合由⼀个根结点加上两棵别称为左子树和右子树的⼆叉树组成或者为空。二叉树是树形结构的一种,也可以说是对树的结构加以限制形成二叉树。
 
从上图可以看出二叉树具备以下特点:
1. ⼆叉树不存在度大于 2 的结点。(二叉树只存在度为0、1、2的节点)
2. ⼆叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。(这里的有序是指左右孩子是有区分的)
注意:对于任意的二叉树都是由以下几种情况复合而成的。

 

第一个是空树(度为0),第二个叫只有根节点的二叉树,第三个叫做只有左子树的二叉树,第四个叫做只有右子树的二叉树,第五个叫做左右子树都存在的二叉树。

2.2 特殊的二叉树

2.2.1 满二叉树

⼀个二叉树,除了叶子节点外,如果每⼀个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为 K ,且结点总数是 2 k − 1 ,则它就是满二叉树。

2.2.2 完全⼆叉树

完全⼆叉树是效率很高的数据结构,完全二叉树是由满二叉树引出来的。对于深度为 K 的,有 n 个结点的二叉树,当且仅当其每⼀个结点都与深度为K的满二叉树中编号从 1至  n 的结点⼀⼀对应时称 之为完全二叉树。要注意的是满二叉树是⼀种特殊的完全二叉树。
假设二叉树层次为K,除了第K层外,每层结点的个数达到最大结点数,第K层结点个数不一定达到最大节点数。
这种就不是完全二叉树(完全二叉树结点的顺序是从左到右的)。
总结:
满二叉树一定是完全二叉树,但是完全二叉树不一定是满二叉树。
💡 ⼆叉树性质
根据满二叉树的特点可知:
1)若规定根结点的层数为 1 ,则⼀棵非空二叉树的第i层上最多有 2^i −1 个结点
2)若规定根结点的层数为 1 ,则深度为 的二叉树的最大结点数是 2^h   − 1
3)若规定根结点的层数为 1 ,具有 n 个结点的满⼆叉树的深度 ( log以2为底, n+1 为对数)
(由2^h-1 = n演变而来)

2.3 ⼆叉树存储结构

二叉树⼀般可以使用两种结构存储,⼀种顺序结构,⼀种链式结构。

2.3.1 顺序结构

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有 空间的浪费,完全二叉树更适合使用顺序结构存储。

现实中我们通常把堆(⼀种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,⼀个是数据结构,⼀个是操作系统中管理内存的⼀块区域分段。

2.3.2 链式结构

二叉树的链式存储结构是指,用链表来表示⼀棵⼆叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链。后面课程学到高阶数据结构如红黑树等会用到三叉链。

 

3. 实现顺序结构二叉树

一般堆使用顺序结构的数组来存储数据,堆是⼀种特殊的二 叉树,具有二叉树的特性的同时,还具备其他的特性。

3.1 堆的概念与结构

堆具有以下性质:
堆中某个结点的值总是不大于或不小于其父结点的值;
堆总是⼀棵完全二叉树。
小堆堆顶是堆最小值
堆堆顶是堆最大值
•  存储在数组中的元素不一定是有序的 
💡 叉树性质
对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从
0 开始编号,则对于序号为 i 的结点有:
1. i>0 i 位置结点的双亲序号: (i-1)/2 i=0 i 为根结点编号,无双亲结点
2. 2i+1<n ,左孩子序号: 2i+1 2i+1>=n 否则无左孩子
3. 2i+2<n ,右孩子序号: 2i+2 2i+2>=n 否则无右孩子

//定义堆的结构——数组    堆的底层是使用顺序结构数组来实现的
typedef int HPDataType;
typedef struct Heap
{
    HPDataType* arr;
    int size;//有效的数据个数
    int capacity;//空间大小
}HP;

//堆的初始化

void HPInit(HP* php);

堆的销毁
void HPDestroy(HP* php);

堆数据的插入
void HPPush(HP* php, HPDataType x);

//判断链表是否为空

//判断空间是否足够

实现到堆的数据插入之后,不确定是不是真的符合大、小堆存储,这时候就要进行堆的向上调整算法。在这之间,还要用到两个变量交换的函数Add。(只需要比较父结点和左孩子结点,若父结点大于左孩子结点,就交换位置)。

接下来是出堆,而出堆指的就是删除堆顶数据,当我们直接删除堆顶数据时,会导致堆乱套(后一个位置移动到前一个位置处,堆的底层是顺序表),所以不能直接删除堆顶数据。因此,我们必须采取其他的办法。

方法:

最后一个结点的数据和堆顶数据交换,这时让size--;而堆顶数据(交换后为最后一个结点)能直接被删除,而删除后的堆顺序不一定符合大、小堆,所以我们要用到向下调整算法。

//去堆顶
void HPPop(HP* php);

在出堆中,我们必须要保证父结点的值和左右孩子中最小的值去交换(向下调整算法)。

//出堆顶
HPDataType HPTop(HP* php);

//打印元素判断代码是否正确

附源代码: 

Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义堆的结构——数组    堆的底层是使用顺序结构数组来实现的
typedef int HPDataType;
typedef struct Heap
{
    HPDataType* arr;
    int size;//有效的数据个数
    int capacity;//空间大小
}HP;

void HPInit(HP* php);
void HPDestroy(HP* php);
void HPPush(HP* php, HPDataType x);
//去堆顶
void HPPop(HP* php);
//判空
bool HPEmpty(HP* php);
//出堆顶
HPDataType HPTop(HP* php);

void Swap(int* x, int* y);

 Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
void HPInit(HP* php)
{
    assert(php);
    php->arr = NULL;
    php->capacity = php->size = 0;
}

void HPDestroy(HP* php)
{
    assert(php);
    if (php->arr)
    {
        free(php->arr);
    }
    php->arr = NULL;
    php->capacity = php->size = 0;
}
void Swap(int* x, int* y)
{
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
void AdjustUp(HPDataType *arr, int child)
{
    int parent = (child - 1) / 2;
    while (child > 0)//不需要等于,child只要走到根节点的位置,根节点没有父节点不需要交换
    {
        if (arr[child] < arr[parent])
        {
            Swap(&arr[parent], &arr[child]);
            child = parent;
            parent = (child - 1) / 2;
        }
        else
        {
            break;
        }
    }
}
void HPPush(HP* php, HPDataType x)
{
    assert(php);
    //判断空间是否足够
    if (php->capacity == php->size)
    {
        //扩容
        int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
        HPDataType* tmp = (HPDataType*)realloc(php->arr, newCapacity * sizeof(HPDataType));
        if (tmp == NULL)
        {
            perror("realloc fail!");
            exit(1);
        }
        php->arr = tmp;
        php->capacity = newCapacity;
    }
    php->arr[php->size] = x;
    AdjustUp(php->arr, php->size);
    php->size++;
}
void AdjustDown(HPDataType* arr, int parent, int n)
{
    int child = 2 * parent + 1;
    while (child < n)
    {
        //找左右孩子中最小的
        if (child +1 < n && arr[child] > arr[child + 1])
        {
            child++;
        }
        if (arr[parent] > arr[child])
        {
            Swap(&arr[child], &arr[parent]);
            parent = child;
            child = 2 * parent + 1;
        }
        else
        {
            break;
        }
    }
}

void HPPop(HP* php)
{
    assert(php && php->size);
    Swap(&php->arr[0], &php->arr[php->size - 1]);
    --php->size;
    AdjustDown(php->arr, 0, php->size);
}
//判空
bool HPEmpty(HP* php)
{
    assert(php);
    return php->size == 0;
}
HPDataType HPTop(HP* php)
{
    assert(php && php->size);//堆顶的元素不能为空
    return php->arr[0];
}

text.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
void text01()
{
    HP hp;
    HPInit(&hp);
    int arr[] = { 17,20,10,13,19,15 };

    for (int i = 0; i < 6; i++)
    {
        HPPush(&hp, arr[i]);
    }
    while (!HPEmpty(&hp))
    {
        printf("%d ", HPTop(&hp));
        HPPop(&hp);
    }
    HPDestroy(&hp);
}
int main()
{
    text01();
    return 0;
}
 

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

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

相关文章

GD32相较于STM32的优劣势-完全总结

优势 1.更高的主频 GD32单片机的主频可以达到108MHz&#xff0c;‌而STM32的最大主频为72MHz&#xff0c;‌这意味着GD32在代码执行速度上具有优势&#xff0c;‌适合需要快速处理数据的场景 2.更低的内核电压 GD32的内核电压为1.2V&#xff0c;‌而STM32的内核电压为1.8V。…

《系统架构设计师教程(第2版)》第12章-信息系统架构设计理论与实践-05-信息系统架构案例分析

文章目录 1. 价值驱动的体系结构——连接产品策略与体系结构1.1 价值模型1&#xff09;概述2&#xff09;价值驱动因素3&#xff09;传统方法识别价值模型的缺陷&#xff08;了解即可&#xff09; 1.2 体系结构策略&#xff08;挑战&#xff09;1&#xff09; 优先级的确定2&am…

【C++】动态内存管理与模版

目录 1、关键字new&#xff1a; 1、用法&#xff1a; 2、理解&#xff1a; 3、与malloc的相同与不同&#xff1a; 1、相同&#xff1a; 2、不同&#xff1a; 2、模版初阶&#xff1a; 1、函数模版&#xff1a; 1、概念&#xff1a; 2、关键字&#xff1a;template&…

微信公众号获取用户openid(PHP版,snsapi_base模式)

微信公众号获取用户openid的接口有2个&#xff1a;snsapi_base、snsapi_userinfo 详情见微信公众号开发文档&#xff1a;https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 本文介绍用PHP方式调用snsapi_base接口获取微信用户…

苦学Opencv的第十一天:图像的形态学操作

Python OpenCV从入门到精通学习日记&#xff1a;图像的形态学操作 前言 图像形态学是图像处理中的一个重要分支&#xff0c;主要关注图像中物体的形状和结构。通过形态学操作&#xff0c;我们可以对图像进行有效的分析和处理&#xff0c;例如图像的腐蚀与膨胀、开运算与闭运算…

ansible基础讲解和加密文件讲解

ansible最重要的三个文件 /etc/ansible/ansible.cfg #####ansible的配置文件 /etc/ansible/host ##清单文件inventory ansible-navigator.yml ####以yml结尾的文件可以理解为conf结尾的文件&#xff0c;是配置文件&#xff0c;用于设置剧本playbook playbook讲解 以.yml结…

vue3中计算属性

假如需要修改,需要使用get,set let a ref(111) import {computed} from vue let changeimg computed({get(){return a},set(val){a.value val}}) 如果不需要修改 let a ref(111) import {computed} from vue let changeimg computed(() >{return a })

135.分发糖果,遍历方向+candy选取的详解

力扣135分发糖果 题目思路代码 题目 https://leetcode.cn/problems/candy/description/ 老师想给孩子们分发糖果&#xff0c;有 N 个孩子站成了一条直线&#xff0c;老师会根据每个孩子的表现&#xff0c;预先给他们评分。 你需要按照以下要求&#xff0c;帮助老师给这些孩子…

WordPress原创插件:自定义文章标题颜色

插件设置截图 文章编辑时&#xff0c;右边会出现一个标题颜色设置&#xff0c;可以设置为任何颜色 更新记录&#xff1a;从输入颜色css代码&#xff0c;改为颜色选择器&#xff0c;更方便&#xff01; 插件免费下载 https://download.csdn.net/download/huayula/89585192…

【一图流】Git下载与安装教程

下载Git Git官网&#xff1a;https://git-scm.com/?hlzh-cn 安装Git

UE5 C++跑酷练习(Part2)

一.首先GameMode里有Actor数组&#xff0c;组装直线路&#xff0c;和左右路 #include "CoreMinimal.h" #include "GameFramework/GameModeBase.h" #include "RunGANGameMode.generated.h"UCLASS(minimalapi) class ARunGANGameMode : public AG…

揭秘企业为何钟情定制红酒:品牌形象与不同的礼品的双重魅力

在商务世界的广阔天地里&#xff0c;红酒不仅仅是一种饮品&#xff0c;更是一种传递情感、展示品味的不同媒介。近年来&#xff0c;越来越多的企业开始钟情于定制红酒&#xff0c;其中洒派红酒&#xff08;Bold & Generous&#xff09;通过其品质和个性化的定制服务&#x…

网络访问(Socket/WebSocket/HTTP)

概述 HarmonyOS为用户提供了网络连接功能&#xff0c;具体由网络管理模块负责。通过该模块&#xff0c;用户可以进行Socket网络通滚、WebSocket连接、HTTP数据请求等网络通信服务。 Socket网络通信&#xff1a;通过Socket(嵌套字)进行数据通信&#xff0c;支持的协议包括UDP核…

《追问试面试》系列开篇

我们不管做任何事情&#xff0c;都是需要个理由&#xff0c;而不是盲目去做。 为什么写这个专栏&#xff1f; 就像我们被面试八股文时&#xff0c;市面上有很多面试八股文&#xff0c;随便一个八股文都是500&#xff0c;甚至1000面试题。诸多面试题&#xff0c;难道我们需要一…

基于微信小程序+SpringBoot+Vue的资料分享系统(带1w+文档)

基于微信小程序SpringBootVue的资料分享系统(带1w文档) 基于微信小程序SpringBootVue的资料分享系统(带1w文档) 校园资料分享微信小程序可以实现论坛管理&#xff0c;教师管理&#xff0c;公告信息管理&#xff0c;文件信息管理&#xff0c;文件收藏管理等功能。该系统采用了Sp…

vue3中element tabs标签页 tab-click事件无法拿到最新值

element tabs标签页有2个常用的事件方法&#xff0c;tab-click 和 tab-change tab-click事件 tab-click事件&#xff1a;当用户点击Tab标签时触发&#xff0c;有2个返回参数&#xff0c; (pane: TabsPaneContext, ev: Event) pane.props.name 中可以获取到最新的tab页签绑定值 …

jenkins参数化构建在UI中定义脚本中使用

先看配置&#xff1a; 流水线脚本&#xff1a; pipeline {agent {//label "${server}"label "${28}"}stages {stage(Hello) {steps {echo "--------------------------"// 只有这个可以输出变量echo "${character_argument}"echo &q…

网络通信---TCP协议1

今日内容 三次握手: 指建立tcp连接时&#xff0c;需要客户端和服务端总共发送三次报文确认连接。 四次挥手&#xff1a; 断开一个tcp连接&#xff0c;需要客户端和服务端发送四个报文以确认断开。 编程模型 TCP报文 客户端 服务端

E21.“详解函数递归”文中的趣味练习的答案

详解函数递归原文 高考标答&#xff1a; 思路&#xff1a; 代码实现&#xff1a; //这里取αn1 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> double function(double n) {if (1 n){return 2;}else{return 1 1.0 / (function(n - 1));} } int main() {int n …

大自然的传奇——龙宫

我们1小时后就到了龙宫&#xff0c;导游给我们买票去了&#xff0c;让我们去观景台上&#xff0c;看游客中心后面“龙”字草书。龙字田采用两种农作物套种&#xff0c;按季节区分&#xff0c;春天由油菜花和蚕豆进行套种&#xff0c;秋天由黑糯米和一般水稻进行套种。我们来的夏…