当前位置: 首页 > news >正文

二叉树----规矩森严的“家族树”(第11讲)

一. 树

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的孩子结点。

   ③结点的度:一个结点有几个孩子,他的度就是多少;eg:A的度是6,F的度为2。

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

   ⑤叶子结点/终端结点:度为0的结点称为叶子结点;如上图:B C H I为叶子结点。

   ⑥分支结点/非终端结点:度不为0的结点;如上图:D E F G为分支结点。

   ⑦兄弟结点:具有相同父结点的结点称为兄弟结点(亲兄弟),如B与C。

   ⑧结点的层次:从根结点开始为第一层,根的子结点为第二层,以此类推...

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

   ⑩结点的祖先:从根结点到该结点所经分支的所有结点,如上图:A是所有结点的祖先。

   

1.3 树的表示

孩子兄弟表示法:

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

struct TreeNode{struct Node* child; //从左边开始第一个孩子结点struct Node* brother;//指向其右边的下一个兄弟结点int data;
};

1.4 树形结构实际运用场景

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


二. 二叉树

2.1 概念与结构

        在树形结构中,我们最常用的就是二叉树,一棵二叉树是结点的一个有限集合,该集合由一个根结点加上两棵别称为左子树和右子树的二叉树组成或者为空。

二叉树的特点:

①二叉树不存在度大于2的结点

②二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

注意:对于任意的二叉树都是由以下几种情况复合而成:

 现实中的二叉树:

2.2 特殊的二叉树

2.2.1 满二叉树

       一个二叉树,如果每一层结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,每层结点个数都是2^(k-1),根据等比数列求和公式,结点总数是2^k-1,则这个树是满二叉树

2.2.2 完全二叉树

    1.完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树引出来的。完全二叉树除了最后一层,其他每层节点个数都达到最大;完全二叉树的结点从左到右依次排列

      

2.2.3 二叉树的性质

根据满二叉树的特点可知:

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

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

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

2.3 二叉树存储结构

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

2.3.1 顺序结构

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

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

2.3.2 链式结构

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


三. 实现顺序结构二叉树

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

3.1 堆的概念与结构

    ①堆中某个结点的值总是不大于或者不小于其父结点的值。

    ②堆总是一棵完全二叉树。堆顶是最值(最大值或者最小值)。

1.二叉树的性质

    对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0开始编号,则对于序号为i的结点有:

    ①若i>0,i位置结点的双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点。

    ②若2i+1<n,左孩子序号:2i+1,2i+1>=n,否则无左孩子。

    ③若2i+1>n,右孩子序号:2i+2,2i+2>=n,否则无右孩子。

3.2 堆的实现

1.向上调整算法

   ①先将元素插入到堆的末尾,即最后一个孩子之后。

   ②插入之后如果堆的性质遭到破坏,将新插入结点顺着其父亲往上调整到合适的位置即可。

2.向下调整算法

   ①将堆顶元素与堆中最后一个元素进行交换。

   ②删除堆中最后一个元素。

   ③将堆顶元素向下调整到满足堆特性为止。

注:向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

#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;//1.初始化
void HPInit(HP* php);
//2.销毁
void HPDestory(HP* php);
//3.往堆中插入数据
void HPPush(HP* php, HPDataType x);
//4.打印
void HPPrint(HP* php);
//5.判断堆是否为空
bool HPEmpty(HP* php);
//6.删除堆中的元素
void HPPop(HP* php);
//7.取堆顶
HPDataType HPtop(HP* php);
//8.向下调整算法
void Adjustdown(HPDataType* arr, int parent, int n);
//9.向上调整算法
void Adjustup(HPDataType* arr, int child);
#include "Heap.h"
//1.初始化
void HPInit(HP* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}
//2.销毁
void HPDestory(HP* php)
{assert(php);if (php->arr)free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}
//4.打印
void HPPrint(HP* php)
{for (int i = 0; i < php->size; i++){printf("%d ", php->arr[i]);}printf("\n");
}
//交换
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){if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
//向下调整算法
void Adjustdown(HPDataType* arr, int parent,int n)
{int child = parent * 2 + 1;while (child < n){//找最大的孩子if (child+1<n&&arr[child] < arr[child + 1]){child++;}if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
//3.往堆中插入数据
void HPPush(HP* php,HPDataType x)
{//1.当空间不够时if (php->size == php->capacity){int newCapacity = php->capacity ? 4 : 2 * php->capacity;HP* tmp = (HPDataType*)realloc(php->arr, newCapacity*sizeof(HPDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}php->arr = tmp;php->capacity = newCapacity;}//2.当空间足够时php->arr[php->size++] = x;//3.向上调整Adjustup(php->arr, php->size - 1);
}
//5.判断堆是否为空
bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}
//6.删除堆中的元素
void HPPop(HP* php)
{assert(!HPEmpty(php));Swap(&php->arr[0], &php->arr[php->size - 1]);--php->size;//堆顶数据需要向下调整Adjustdown(php->arr, 0, php->size);
}
//7.取堆顶
HPDataType HPtop(HP* php)
{assert(!HPEmpty(php));return php->arr[0];
}

3.3 向下调整算法建堆

排升序---建大堆   排降序---建小堆

//实际的堆排序--使用堆结构的思想
void Heapsort(int* arr, int n)
{//对乱序数组进行建堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){Adjustdown(arr, i, n);}//此时如上--乱序的数组已经变成了有效的堆int end = n - 1;while (end > 0){Swap(&arr[0], &arr[n - 1]);Adjustdown(arr, 0, end);end--;}
}

3.4 向上调整算法建堆

void Heapsort(int* arr, int n)
{//向上调整算法建堆for (int i = 0; i < n; i++){Adjustup(arr, i);}int end = n - 1;while (end > 0){Swap(&arr[0], &arr[n - 1]);Adjustdown(arr, 0, end);end--;}
}

3.5 向上/下调整算法的时间复杂度

  1.向上调整算法的时间复杂度:假设结点个数为n,如何求解二叉树的层次?2^k-1=n 则2^k=n+1,节点向上调整了几层,while就循环了几次,所以时间复杂度为O(logN)。

  2.向下调整算法的时间复杂度:节点向下调整了几层,while就循环了几次,所以时间复杂度也为O(logN)。

  3.向上调整算法建堆的时间复杂度O(n*logn)

4.向下调整算法建堆的时间复杂度O(n)(计算过程原理同3.)


喜欢的朋友们可以一键三连喔~

http://www.dtcms.com/a/545239.html

相关文章:

  • 随州网站建设有哪些南昌网站建设是什么意思
  • php免费企业网站模板祥云县住房和城乡建设网站
  • 宏观经济走势对网民互联网消费行为的影响:基于开源链动2+1模式AI智能名片S2B2C商城小程序的实证分析
  • 网站开发 环境品牌设计概念
  • 网站建设加盟培训网站内图片变换怎么做
  • Linux设置服务开机自启动脚本
  • wordpress适合做大型网站吗潍坊专业人员继续教育
  • openpnp - 如果出现不正常的情况,需要将设备和主板重新上电
  • 【音视频】WebRTC连接建立流程详解
  • 从零开始的C++学习生活 17:异常和智能指针
  • OceanBase 分布式数据库的 ETL 实践:从抽取到实时分析
  • 在谷歌上做国际网站支持wordpress的主机
  • Prometheus 详解:从原理到实战,打造企业级云原生监控体系
  • 使用SSE进行实时消息推送!替换WebSocket,轻量好用~
  • YOLO V2全面解析:更快、更准、更强大的目标检测算法
  • 小白python入门 - 12. Python集合——无序容器的艺术与科学
  • 墨刀做的网站设计阿里云域名出售
  • 悬垂引用的攻防战:Rust 如何从根源杜绝内存访问灾难
  • IDEA好用的插件
  • 湖南住房与城乡建设部网站顺义公司网站建设
  • Matplotlib 直方图
  • RocketMQ核心架构解析与实战指南
  • Excel怎么制作下拉菜单?
  • 如何做后台网站的教程WordPress+百度+主动
  • Faster-Whisper唤醒词检测程序设计实战1
  • MPP文件处理组件Aspose.Tasks教程:使用Python在Excel中打开MPP文件
  • Optimum:onnx模型量化
  • C++ 鸭科夫手柄适配
  • dubbo和springcloud的差别
  • Linux系统编程——目录操作函数