数据结构之树(1)

news2024/11/25 5:19:12

课程:b站王道数据结构 5.1.1 树的定义和基本术语_哔哩哔哩_bilibili

写在前面:基础不牢,地动山摇。。

一、树

1、概念

树是n(n>=0)个结点的有限集合,n=0时,称为空树

非空树的特性

有且仅有一个根节点

叶子结点”(或终端结点):没有后继的结点

分支结点”(或非终端结点):有后继的结点

除了根节点外,任何一个结点都有且仅有一个前驱

每个结点可以有0个或多个后继

当n>1时,其余结点可分为m(m>0)个互不相交的有限集合T1,T2,…,Tm,其中每个集合本身又是一棵树,并称为根结点的子树

树是一种递归定义的数据结构

结点之间的关系

祖先结点  子孙结点  双亲结点(父结点) 孩子结点  兄弟结点  堂兄弟结点

两个结点之间的路径  路径长度

结点、树的属性描述

结点的层次(深度)——从上往下数

结点的高度——从下往上数

树的高度(深度)——总共多少层

结点的度——有几个孩子(分支)非叶子结点>0 叶子结点=0

树的度——各结点的度的最大值

有序树、无序树

有序树,逻辑上看,树中结点的各子树从左至右是有次序的,不能互换

森林

m(m>=0)棵互不相交的树的集合

2、性质

(1)结点和度

结点数=总度数+1(总度数其实就是分支的总数量,只有根节点头上没有分支,所以是总度数+1)

度为m的树、m叉树的区别

树的度——各结点的度的最大值

度为m的树  任意结点的度<=m(最多m个孩子) 

至少有一个结点度=m(有m个孩子)一定是非空树,至少有m+1个结点 

m叉树——每个结点最多只能有m个孩子的树

任意结点的度<=m(最多m个孩子)

允许所有结点的度都<m  可以是空树

度为m的树第i层至多有$m^{i-1}$个结点(i>=1)

eg m=3, 第1层1个结点,第2层3个结点,第3层3*3=$3^{3-1}$......

m叉树第i层至多有$m^{i-1}$个结点(i>=1)

(2)结点和高度

高度为h的m叉树至多有$\frac{m^h-1}{m-1}$个结点

计算过程:$1, m , m^2, ... ,m^{h-1}$ 等比求和 $\frac{1-m^h}{1-m}$

高度为h的m叉树至少有h个结点,高度为h、度为m的树至少有h+m-1个结点

(除了某一层为m,其他每一层为1)

具有n个结点的m叉树的最小高度为$log_m(n(m-1)+1)$

计算过程:$\frac{m^h-1}{m-1}=n$

3、存储

①双亲表示法(顺序)

5.4_1_树的存储结构_哔哩哔哩_bilibili

struct TreeNode{
    int data;
    int parent;
}
  • 优点:查指定结点的双亲很方便  缺点:查指定结点的孩子只能从头遍历

②孩子表示法(顺序+链式)

struct CTNode{
    int child;//孩子结点在数组中的位置
    struct CTode *next;//下一个孩子
};
typedef struct{
    int data;
    struct CTNode *firstChild;//第一个孩子
}CTBox;

③孩子兄弟表示法(链式)

typedef struct CSNode{
    int data;
    struct CSNode *firstchild,*nextsibling;//第一个孩子和右兄弟指针
}CSNode,*CSTree;

二、二叉树

1、概念

每个结点至多只有两棵子树

左右子树不能颠倒(二叉树是有序树

二叉树的五种状态 :空二叉树  只有左子树  只有右子树  只有根节点  左右子树都有

2、性质

(1)n0=n2+1

设非空二叉树中度为0、1和2的结点个数分别为n0、n1和n2,则n0=n2+1(叶子结点比二分支结点多一个)

计算过程:

n=n0+n1+n2  n=n1+2*n2+1 -> n0=n2+1

(2)第i层至多有$2^{i-1}$个结点(i>=1)

(3)高度为h的二叉树至多有$2^{h}-1$个结点(满二叉树)

高度为h的m叉树至多有$\frac{m^h-1}{m-1}$个结点

3、几种特殊的二叉树

(1)满二叉树

高度为h,且含有$2^{h-1}$个结点的二叉树

只有最后一层有叶子结点

不存在度为1的结点

按层序从1开始编号,结点i的左孩子为2i,右孩子为2i+1, 结点i的父节点为 i/2

(2)完全二叉树

当且仅当每个结点都与高度为h的满二叉树中编号1~n的结点一一对应时,称为完全二叉树

只有最后两层可能有叶子结点

最多只有一个度为1的结点

按层序从1开始编号,结点i的左孩子为2i,右孩子为2i+1, 结点i的父节点为[i/2]

i<=[n/2]为分支结点,i>[n/2]为叶子结点

(因为n的父结点是[n/2])

如果某结点只有一个孩子,那么一定是左孩子

性质:

① 具有n个(n>0)结点的完全二叉树的高度h为$[log_2(n+1)]$$[log_2n]+1$

计算过程:

高度为h最大结点数:$2^h-1=n$

最小结点数:$2^{h-1}-1+1=n$

② n0=n2+1->n0+n2一定是奇数

完全二叉树最多只有一个度为1的结点,即n1=0或1

(3)二叉排序树

左子树上所有结点的关键字均小于根结点的关键字

右子树上所有结点的关键字均大于根结点的关键字

左子树和右子树又各是一棵二叉排序树

可用于元素的排序、搜索

(4)平衡二叉树

树上任一结点的左子树和右子树的深度之差不超过1

有更高的搜索效率

4、存储结构

(1)顺序存储

就是用数组来描述

定义结点

struct TreeNode{
int data;
bool isEmpty;
}

存放在数组

TreeNode t[MaxSize];
for(int i=1;i<MaxSize;i++){//可以让第一个位置空缺,保证数组下标和结点编号一致
    t[i].isEmpty=true;//初始化时所有结点标记为空
    t[i].data
}

顺序存储中一定要把二叉树的结点编号与完全二叉树对应起来

结论:二叉树的顺序存储结构,只适合存储完全二叉树

(2)链式存储

用链表

定义结点

struct TreeNode{
    int data;
    struct TreeNode *lchild, *rchild;//孩子表示法

    TreeNode(int val):data(val),left(nullptr),right(nullptr){} 
};

创建二叉树

TreeNode* Create(){
    int val; 
    cin >> val;
    if(val == '#') return nullptr; //空结点

    TreeNode* newNode = new TreeNode(val);
    //cout<<"enter lchild of :"<<val ;
    newNode -> lchild = Create();
    //cout<<"enter rchild of :"<<val ;
    newNode -> rchild = Create();

    return newNode;
}
//三叉链表——方便找父结点
    typedef struct TreeNode{
    int data;//数据域
    struct TreeNode *lchild,*rchild;//左、右孩子指针
    struct TreeNode *parent;// 父结点指针
    }BiNode,*BiTree;

5、二叉树的遍历 重点!!

(1)先序遍历

根 -> 左 -> 右

  - + a * b - c d / e f

递归

void PreOrderRecursive(TreeNode* root){
    if ( root == nullptr ) return;
    cout << root->data << " ";
    PreOrderRecursive (root -> lchild);
    PreOrderRecursive (root -> rchild);
}

非递归

众所周知,递归和栈有点关系👀

void PreOrderNotRecursive(TreeNode* root){
    if ( root == nullptr ) return;
    stack<TreeNode*>s;
    s.push(root);//入栈
    while(!s.empty()){
        TreeNode* node = s.top();//栈顶元素

        s.pop();//出栈
        cout << node->data ;
        if(node -> rchild )//右晚出 
            s.push(node -> rchild);
        if(node ->lchild )//左先出 
            s.push(node -> lchild);
    }

这里可以看:45 二叉树的非递归遍历代码实现_哔哩哔哩_bilibili

(2)中序遍历

 a + b * c - d - e / f

递归

void InOrderRecursive(TreeNode* root){
    if ( root == nullptr ) return;

    InOrderRecursive (root -> lchild);
    cout << root->data << " ";
    InOrderRecursive (root -> rchild);
}

非递归

void InOrderNotRecursive(TreeNode* root){
    //if ( root == nullptr ) return;

    stack<TreeNode*>s;
    TreeNode* curr = root;
    while(curr || !s.empty())
        { while(curr){
            s.push(curr);
            curr = curr ->lchild;
            }
          curr =s.top();
          s.pop();
          cout<< curr -> data ;
          curr= curr->rchild;
        }
   }

(3)后序遍历

 a b c d - * + e f / -

 递归

void PostOrderRecursive(TreeNode* root){
    if ( root == nullptr ) return;

    PostRecursive (root -> lchild);
    PostRecursive (root -> rchild);
    cout << root->data << " ";
}

非递归

void PostOrderNotRecursive(TreeNode* root){
    if ( root == nullptr ) return;
    stack<TreeNode*>s1,s2;
    s1.push(root);
    while(!s1.empty())
    { TreeNode * node =s1.top(); s1.pop(); s2.push(node);
      if(node->lchild) s1.push(node->lchild);}
      if(node->rchild) s1.push(node->rchild);}
    }
    while(!s2.empty())
    {cout<<s2.top()->data; s2.pop()}
}

(4)层次遍历

用队列

void levelOrder(TreeNode* root){
    arrayQueue<TreeNode*>q;
    while(root ! =nullptr)
    {   
        cout<< root->data;//visit(root)
        if(root->lchild) q.push(root->lchild);
        if(root->rchild) q.push(root->rchild);

        try{t=q.front();}
        catch (queueEmpty){return;}
        q.pop();
    }
}

6、由遍历序列构造二叉树 重点!!

唯一确定一棵二叉树的几种情况:

前序+中序

            左子树     右子树

左子树          跟       右子树

eg 

Pre   A  D  B  C  E

In     B  D  C   A  E

Pre  D  B  C

In    B  D  C

后序+中序

层序+中序

同理,都是找根结点,左子树右子树去分析

7、应用

(1)求树的深度

int TreeHeight(TreeNode* root) {
    if (root == nullptr) {
        return 0;
    }
    int leftHeight = TreeHeight(root->lchild);
    int rightHeight = TreeHeight(root->rchild);
    return max(leftHeight, rightHeight) + 1;
}

(2)求叶子总数

int CountLeaves(TreeNode* root) {
    if (root == nullptr) {
        return 0;
    }
    if (root->lchild == nullptr && root->rchild == nullptr) {
        return 1;
    }
    return CountLeaves(root->lchild) + CountLeaves(root->rchild);
}

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

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

相关文章

智能家居有哪些产品?生活中常见的人工智能有哪些?

智能家居有哪些产品? 1、智能照明设备类&#xff1a;智能开关、智能插座、灯控模块、智能空开、智能灯、无线开关。 2、家庭安防类&#xff1a;智能门锁、智能摄像机、智能猫眼、智能门铃。 3、智能传感器类&#xff1a;烟雾传感器、可燃气体传感器、水浸传感器、声光报警器…

CentOS7系统配置Yum环境

新安装完系统的服务器往往缺少我们常用的依赖包&#xff0c;故需要设置好yum源&#xff0c;方便软件安装&#xff0c;以下是CentOS7为例&#xff0c;系统安装后yum默认安装。 //备份之前的配置文件 mv /etc/yum.repos.d /etc/yum.repos.d.bak mkdir -p /etc/yum.repos.d 1…

鸿蒙开发之ArkUI 界面篇 十八 京东app登录界面实现

鸿蒙UI实现某东App登录界面,如下图鲜果,我们先分析整体架构是什么! 我们整体架构分析,分为区域1、2、3、4、5、6、7、8、9区域,下图: 8个区域的整体方向是垂直的,容器使用的是Column,区域1使用的是子容器Row,左边是Image,右边是Text,区域2是Image,区域3第一感觉是…

欧姆龙(Omron)协议解析

1. 协议概述    欧姆龙(Omron)是来自日本的知名电子和自控设备制造商&#xff0c;其中、小型PLC在国内市场有较高的占有率&#xff0c;有CJ、CM等系列。PLC可以支持Fins、Host link等协议进行通信。 支持以太网的欧姆龙PLC CPU、以太网通信模块根据型号的不同&#xff0c;一般…

dockertop提示Failed to fetch extensions

解决办法&#xff1a;重装dockertop 第一步&#xff1a;卸载当前的dockertop 如果卸载过程中存在AlibabaProtect的相关软件关不掉&#xff0c;那么参考这篇文章&#xff1a;卸载AlibabaProtect 第二步&#xff1a;删除C:\Program Files路径下的Docker文件夹 第三步&#xff1…

代码随想录Day 62|Floyd 算法精讲、A \* 算法精讲 (A star算法),题目:97. 小明逛公园、127. 骑士的攻击

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 图论part11Floyd 算法精讲题目&#xff1a;97. 小明逛公园解题思路&#xff1a; A \* 算法精讲 &#xff08;A star算法&#xff09;题目&#xff1a;127. 骑士的攻击问题描述算法选择解题思路C代…

什么是重卡充电桩?

有什么广告&#xff1f;没有广告&#xff0c;纯纯的介绍。 在政策与市场双重驱动下&#xff0c;充电桩市场已经开启加速模式&#xff0c;行业的火苗越烧越旺。同时&#xff0c;随着新能源重卡的广泛普及&#xff0c;重卡充电桩也迎来了新的发展机遇。 此种背景下 &#xff0c…

Pikachu-敏感信息泄露

直接访问页面&#xff0c;查看页面源码 如&#xff1a;返回了测试账号信息&#xff1b; 例如&#xff1a;返回服务端的信息

仿RabbitMQ实现消息队列三种主题的调试及源码

文章目录 开源仓库和项目上线广播交换模式下的测试直接交换模式下的测试主题交换模式下的测试 开源仓库和项目上线 本项目已开源到下面链接下的仓库当中 仿RabbitMQ实现消息队列 广播交换模式下的测试 消费者客户端 在进行不同测试下&#xff0c;消费者客户端只需要改变交换机…

【Blender Python】4.获取场景对象的几种方式

概述 有时候我们需要获取场景中已经添加或存在的对象。本节就总结在Blender Python中获取场景中对象的一些方法。 通过名称获取 py.data的objects()方法返回一个对象集合&#xff0c;可以使用键名或者下标形式获取具体的对象。 在默认新建的场景中&#xff0c;存在三个对象…

初始Linux(二)基础命令

前言&#xff1a; 之前那一篇我们已经介绍了一部分的基础命令&#xff0c;当然那只不过是九牛一毛&#xff0c;本篇我们继续介绍一些比较重要且需要掌握的基础命令。 mv命令&#xff1a; 其实这个命令有两个功能&#xff0c;一个是移动&#xff08;剪切&#xff09;文件&#…

可查询全部快递api接口分析

使用三方平台该API接口需要先注册后申请此API接口。申请后可直接在线请求接口数据。 该api接口可自动识别单号信息。调用简单方便&#xff0c;性价比高&#xff0c;一条链接即可。 API接口地址为&#xff1a;https://www.tanshuapi.com/market/detail-68 其中&#xff0c;KEY…

掌握嵌套子查询:复杂 SQL 中 * 列的准确表列关系

在日常开发中&#xff0c;我们常常需要对复杂的 SQL 进行数据血缘分析。 本文重点讨论在具有 * 列的嵌套子查询中建立表和列之间正确关系的挑战。使用 Teradata SQL 代码示例来说明该过程。 本文聚焦于一个别名为 SUBSCRIBER_ 的子查询及其派生的列&#xff0c;这些列在外层查…

【Canvas与艺术】玻璃光小红灯

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>玻璃光红灯Draft1</title><style type"text/css"…

RFID学习

24.10.5学习目录 一.简介1.组成2.RFID协议3.RFID卡 一.简介 RFID被称为无线射频识别&#xff0c;其是一种通信技术&#xff0c;通过无线电讯号耦合识别特定目标并读写相关数据&#xff1b; RFID主要位于典型物联网架构中的感知层&#xff0c;其因为具有非接触式特性&#xff…

数据结构与算法篇(树 - 常见术语)

目录 一、什么是树&#xff1f; 二、相关术语 根结点 边 叶子结点 兄弟结点 祖先结点 结点的大小 树的层 结点的深度 结点的高度 树的高度 斜树 一、什么是树&#xff1f; 树是一种类似于链表的数据结构&#xff0c;不过链表的结点是以线性方式简单地指向其后继结…

STM32驱动直流电机

stm32通过PWM控制直流电机的方向和速度。 小直流电机需要几百毫安的电流&#xff0c;单片机只能提供几毫安的电流。电机内线圈转动时切割磁感线以及电机内转子线圈的电感效应都会产生反电动势&#xff0c;损坏芯片。 电机驱动芯片能够作为STM32驱动电机的帮手。 SLEEP暂停工作…

Linux环境下的日志文件的实现

目录 日志 相关函数 time函数 localtime函数 va_list类型 vsnprintf函数 宏支持可变参数 __FILE__和__LINE__ 完整代码 Log.hpp 标记黏合操作符##&#xff08;重点&#xff09; LockGuard.hpp 日志 基本概念&#xff1a;用于记录软件运行时的信息&#xff0c…

数据结构与算法——Java实现 30.合并多个有序链表 小顶堆实现

后来我们都走了很久&#xff0c;远到提及往事时&#xff0c; 总会加上once upon a time —— 24.10.6 23. 合并 K 个升序链表 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 示例 1&#xff1…

linux桌面软件(wps)内嵌到主窗口后的关闭问题

程序测试环境是&#xff1a;slackware系统&#xff0c;属于linux系统&#xff0c;有桌面&#xff08;Xface Session&#xff09;。系统镜像是&#xff1a;slackware64-15.0-install-dvd.iso。qt、c代码实现。 问题描述&#xff1a;延续上一篇文章&#xff0c;将wps软件窗口内嵌…