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

news2024/9/21 12:36:39

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.coloradmin.cn/o/1953205.html

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

相关文章

大语言模型赋能设施农业:透过“智慧大脑“看智能环境调控

&#xff08;文/ 于景鑫 北京市农林科学院&#xff09;在上一篇专栏文章中,我们从宏观视角探讨了大语言模型为设施农业插上腾飞之翼的广阔前景。而要真正实现这一愿景,还需要在微观层面深入剖析LLM的技术原理和应用路径。本文将聚焦设施农业的核心环节之一——环境调控,看看&qu…

【解决】ubuntu20.04 root用户无法SSH登陆问题

Ubuntu root用户无法登录的问题通常可以通过修改‌SSH配置文件和系统登录配置来解决。 修改SSH配置文件 sudo vim /etc/ssh/sshd_config 找到 PermitRootLogin 设置&#xff0c;并将其值更改为 yes 以允许root用户通过SSH登录 保存并关闭文件之后&#xff0c;需要重启SSH服务…

Xshell、XFTP的安装配置及其使用

Xshell、XFTP的安装配置及其使用 Xshell的优点 安全远程连接&#xff1a; Xshell 使用 SSH 协议等安全协议进行远程连接&#xff0c;确保数据传输的加密和安全性。多会话管理&#xff1a; 用户可以同时管理多个远程连接&#xff0c;方便在不同服务器之间切换和操作。终端仿真…

html+css+js 实现马赛克背景按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

python爬虫-事件触发机制

今天想爬取一些政策&#xff0c;从政策服务 (smejs.cn) 这个网址爬取&#xff0c;html源码找不到链接地址&#xff0c;通过浏览器的开发者工具&#xff0c;点击以下红框 分析预览可知想要的链接地址的id有了&#xff0c;进行地址拼接就行 点击标头可以看到请求后端服务器的api地…

女人内裤怎么洗才是最干净?内衣裤洗衣机怎么样?哪个牌子更好?

最近刚好用到一款比较好用的洗内衣裤洗衣机&#xff01;如果你也和我一样有洗内衣裤烦恼的&#xff0c;或者可以看看&#xff01; 内衣裤作为贴身穿的衣服&#xff0c;我是不会把它和外衣一起清洗的&#xff0c;而家里面的大洗衣机已经担起了清洗外衣的工作&#xff01; 朋友们…

React Router-v6.25.1

以下例子是根据vitereactts构建的&#xff0c;使用路由前先安装好这些环境&#xff01;&#xff01;&#xff01;&#xff01; 1、路由的简单使用 首先要创建一个浏览器路由器并配置我们的第一个路由。这将为我们的 Web 应用启用客户端路由。 该main.jsx文件是入口点。打开它…

【杰理蓝牙开发】AC695x 音频部分

本文主要记录 杰理蓝牙audio接口的使用&#xff0c;包括ADC和DAC原理的介绍和API接口的使用。 【杰理蓝牙开发】AC695x 音频部分 0. 个人简介 && 授权须知1. ADC【音频数据采集】硬件部分1.1 单片机引脚1.2 硬件电路设计1.3 MIC 输入通路解释 2. 【DAC】音频信号编解码…

GLSL教程 第9章:计算着色器

目录 9.1 计算着色器的基本概念 计算着色器的主要特点&#xff1a; 9.2 计算着色器的基础知识 1. 创建计算着色器 计算着色器代码&#xff1a; 2. 编译和链接计算着色器 示例代码&#xff1a; 3. 执行计算着色器 示例代码&#xff1a; 9.3 实现并行计算和数据并行处理…

51单片机-第五节-串口通信

1.什么是串口&#xff1f; 串口是通讯接口&#xff0c;实现两个设备的互相通信。 单片机自带UART&#xff0c;其中引脚有TXD发送端&#xff0c;RXD接收端。且电平标准为TTL&#xff08;5V为1,0V为0&#xff09;。 2.常见电平标准&#xff1a; &#xff08;1&#xff09;TTL电…

Mysql中如何实现两列的值互换?给你提供些思路。

文章目录 Mysql中如何实现两列的值互换1、第一感觉此sql应该能处理问题了2、需要一个地方存要替换的值&#xff0c;不然两列搞不定。2.1 加第三列&#xff1f;&#xff08;能解决&#xff0c;但是看起来呆呆&#xff09;2.2 上临时表&#xff08;搞点弯路走走&#xff09; 示例…

C语言画蜡烛图

GPT-4o (OpenAI) 在C语言中&#xff0c;绘制蜡烛图&#xff08;Candlestick Chart&#xff09;不是直接的任务&#xff0c;因为C语言本身不包含高级图形绘制库。然而&#xff0c;可以通过某些图形库来完成这项任务&#xff0c;例如使用GTK、SDL、OpenGL等。 以下是通过GTK库绘…

【iOS】—— retain\release实现原理和属性关键字

【iOS】—— retain\release实现原理和属性关键字 1. retain\reelase实现原理1.1 retain实现原理1.2 release实现原理 2. 属性关键字2.1 属性关键字的分类2.2 内存管理关键字2.2.1 weak2.2.2 assgin2.3.3 strong和copy 2.4 线程安全的关键字2.5 修饰变量的关键字2.5.1常量const…

北京率先建设AI原生城市,力争明年推出百个优秀行业大模型产品

7月26日&#xff0c;《北京市推动“人工智能”行动计划&#xff08;2024-2025年&#xff09;》&#xff08;简称《行动计划》&#xff09;正式向社会发布&#xff0c;新京报记者在北京市发展和改革委员会举行的新闻发布会上获悉&#xff0c;北京将率先建设AI原生城市&#xff0…

基于JSP的班级同学录网站

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSPB/S架构 工具&#xff1a;Eclipse、Mysql 系统展示 首页 管理员功能界面 用户功能界面 论坛管…

ubuntu上部署vue项目到ngixn中+SpringBoot项目+postgresql数据库

文章目录 前提1.Ubuntu上安装ngix2.部署Vue项目2.1上传vue项目2.2.配置 3.Ubuntu上安装Postgres4.部署springboot项目 前提 记一次在ubuntu部署前端vue和后端springboot项目&#xff0c;以及数据库postgresql的安装以及启动、停止等常用的命令。 1.Ubuntu上安装ngix 1、检查…

探索 Python 的色彩世界:Colorama 库深度解析

文章目录 &#x1f308; 探索 Python 的色彩世界&#xff1a;Colorama 库深度解析背景&#xff1a;为何选择 Colorama&#xff1f;Colorama 是什么&#xff1f;如何安装 Colorama&#xff1f;简单库函数使用方法场景应用示例常见问题及解决方案总结 &#x1f308; 探索 Python …

Gartner发布2024年零信任网络技术成熟度曲线:20项零信任相关的前沿和趋势性技术

大多数组织都制定了零信任信息安全策略&#xff0c;而网络是零信任实施领域的顶级技术。此技术成熟度曲线可以帮助安全和风险管理领导者确定合适的技术&#xff0c;以将零信任原则嵌入其网络中。 战略规划假设 到 2026 年&#xff0c;15% 的企业将在企业拥有的局域网上用 ZTNA …

HarmonyOS 质量、测试、上架速浏

1.应用质量要求&#xff1a; 1. 应用体验质量建议: 功能数据完备 功能完备 数据完备 基础体验要求 基础约束 兼容性 稳定性 性能 功耗 安全…