树与二叉树深度剖析(一)

news2024/9/22 11:37:39

一. 树简介

1. 定义

 (1) 树结构是一种非线性存储结构,存储的是具有“一对多”关系的数据元素的集合。

 (2) 树(Tree)是n(n≥0)个节点(Node)的有限集合。在任意一颗非空树中,有且仅有一个特定的成为根(Root)的节点,当n>1时,其余节点分成m(m>0)个互不相交的有限集T1,T2,……,Tm,其中每一个集合本身又是一棵树,并且称为根的子树。树的定义是递归的,即在树的定义中又用到了树的概念,它刻画了树固有的特性,即一棵树有若干个子树构成,而子树又由更小的若干子树构成。

 (3) 树的逻辑特征可以描述为:树中任一节点都可以有0个或多个后继节点(孩子),但至多只能由一个前驱节点(双亲),树中只有根节点没有前驱,叶子节点无后继。

2. 一些概念

 树的节点:使用树结构存储的每一个数据元素都被称为“结点”

 节点的度:对于一个结点,拥有的子树数(结点有多少分支)称为结点的度

 树的度:树内各节点度的最大值

 叶子或终端节点:度为0的节点。

 非终端节点或分支节点:度不为0的节点。

 孩子和双亲:节点的子树的个根成为该节点的孩子。该节点成为孩子的双亲或父亲。

 兄弟:同一个双亲的孩子成为兄弟。

 祖先和子孙:节点的祖先是从根到该节点所经分支上的所有节点。反之,以某节点为根的子树中的任一节点都成为该节点的子孙。

 层数和堂兄弟:层数从根节点开始定义,根为第一层,根的孩子为第二层,其余节点的层数为双亲节点层数+1.双亲在同一层的节点互为堂兄弟。

 树的深度:树节点中的最大层数称为树的深度。

 有序树和无序树:树中节点的各子树从左至右是有次序的成为有序树,否则为无序树。

 森林:有限棵树的集合。对树而言,删去根节点,得到森林。对森林而言,加上根节点变为一棵树

二. 二叉树简介

1. 定义

 本身是有序树,树中包含的各个节点的度不能超过 2,即只能是 0、1 或者 2。

 二叉树有五中基本形态:空二叉树,仅有根节点的二叉树,右子树为空的二叉树,左子树为空的二叉树,左右子树均非空的二叉树。如下图:

2. 二叉树的性质

 (1). 二叉树中,第 i 层最多有 2i-1 个结点。

 (2). 如果二叉树的深度为 K,那么此二叉树最多有 2K-1 个结点。

 (3). 二叉树中,终端结点数(叶子结点数)为 n0,度为 2 的结点数为 n2,则 n0=n2+1。

PS:

 性质 3 的计算方法为:对于一个二叉树来说,除了度为 0 的叶子结点和度为 2 的结点,剩下的就是度为 1 的结点(设为 n1),那么总结点 n=n0+n1+n2。

 同时,对于每一个结点来说都是由其父结点分支表示的,假设树中分枝数为 B,那么总结点数 n=B+1。而分枝数是可以通过 n1 和 n2 表示的,即 B=n1+2*n2。所以,n 用另外一种方式表示为 n=n1+2*n2+1。两种方式得到的 n 值组成一个方程组,就可以得出 n0=n2+1。

3. 满二叉树

 (1). 定义:如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。

 (2). 性质:除了普通二叉树的性质外,还有以下特性

  A. 满二叉树中第 i 层的节点数为 2n-1 个。

  B. 深度为 k 的满二叉树必有 2k-1 个节点 ,叶子数为 2k-1。

  C. 满二叉树中不存在度为 1 的节点,每一个分支点中都两棵深度相同的子树,且叶子节点都在最底层。

  D. 具有 n 个节点的满二叉树的深度为 log2(n+1)。

4. 完全二叉树

 (1).定义:如果二叉树(首先得是个二叉树)中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。

 (2).性质:除了普通二叉树的性质外,还有以下特性

  对于任意一个完全二叉树来说,如果将含有的结点按照层次从左到右依次标号(如图 3a),对于任意一个结点 i ,完全二叉树还有以下几个结论成立:

  A. 当 i>1 时,父亲结点为结点 [i/2] 。(i=1 时,表示的是根结点,无父亲结点)

  B. 如果 2*i>n(总结点的个数) ,则结点 i 肯定没有左孩子(为叶子结点);否则其左孩子是结点 2*i 。

  C. 如果 2*i+1>n ,则结点 i 肯定没有右孩子;否则右孩子是结点 2*i+1 。

更多C++后台开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。

C/C++Linux服务器开发高级架构师/C++后台开发架构师免费学习地址

【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击领取

三. 二叉树存储结构

1.顺序存储结构

(1).含义

 二叉树的顺序存储指用数组进行存储二叉树,但需要注意顺序存储仅仅适用于完全二叉树.

(2).原理

 把一棵二叉树自上而下,从左到右顺序编号,并把编号一次存放在数组内,可以得到如图所示的结果,设满二叉树结点在数组中的索引号为i,那么有如下性质:

1.如果i=0,则结点为根节点,无双亲

2.如果i>0,则双亲节点为(i-1)/2

3.节点为i的左孩子为2i+1,右孩子为2i+2

4.如果i>0,i为奇数时,他是双亲结点的左孩子,他的兄弟为i+1,i为偶数时,他是双亲的右孩子,兄弟为i-1

5.深度为k的满二叉树需要长度为2k-1的数组进行存储。

d42391edf679d8eb4f57df14afa5b852.jpeg

(3). 优点:

 使用数组存放满二叉树是非常方便的,可以根据结点的索引号快速推算出他的双亲,孩子和兄弟,所以他是存储满二叉树最简单,最节省空间的做法。

(4). 缺点:

 结点在数组中的位置反映出结点之间的关系,存储一般二叉树的时候,只需要将数组中的空结点所对应的位置为空即可,如图,这会造成一定的空间浪费,但如果空结点不多,这些浪费可以忽略。

一个深度为k的二叉树需要2k-1个存储空间,当k值很大并且二叉树的空结点很多时,最坏的情况是每层只有一个结点,使用顺序存储显然会造成极大的浪费,这时我们就应该使用链式存储结构来存储二叉树的数据。

2.链式存储结构

(1).含义

 二叉树的链式存储结构可分为二叉链表和三叉链表。二叉链表中,每个结点出了存储本身的数据外,还应该设置两个指针域left和right,分别指向其左孩子和右孩子。如图,这样访问孩子的时间复杂度为O(1),但是访问双亲的复杂度为O(n)

 三叉链表是在二叉链表上的改进,除了存储左右孩子的指针外,另外在加一个指向双亲的指针域parent,如图:

345fc4cd32a9d17e27fe0b95f3cb5b4b.png

四. 二叉树的遍历

1. 说明

 二叉树的遍历就是按照某种顺序对树中的每个节点访问且只能访问一次的过程。树遍历的本质是将非线性化结构线性化,他是二叉树各种运算和操作的实现基础。二叉树遍历分为深度优先遍历和广度优先遍历。

2. 深度优先遍历

 深度优先遍历是使用递归的方法来访问每一个结点的。每棵二叉树都是有数据,左子树,右子树这三个基础部分组成,如果递归遍历这三个部分,也就遍历了整棵二叉树。

(1).先序遍历:访问根节点→访问当前节点的左子树→若当前节点的无左子树,则访问当前节点右子树。 下图遍历结果为:ABDECF

(2).中序遍历:访问当前节点的左子树→访问根节点→访问当前节点右子树。 下图遍历结果为:DBEACF

(3).后序遍历:从根节点出发,依次遍历各节点的左右子树,直到当前节点左右子树遍历完成后,才访问该节点元素。下图遍历结果:DEBFCA

图示:

3.广度优先遍历

 由于二叉树的结点分属不同的层次,因此可以从上而下,从左到右依次按层访问每个节点。他的访问顺序正好和之前所述二叉树顺序存储结构中结点在数组中的存放顺序相吻合。上图的遍历结果为:ABCDEF

代码分享:

二叉树节点类:

/// <summary>
    /// 二叉树节点类
    /// </summary>
    public class Node
    {
        public Node Left { get; set; }  //左孩子

        public Node Right { get; set; }  //右孩子

        public object Data { get; set; } //数据

        public Node(object data)
        {
            this.Data = data;
        }

        public override string ToString()
        {
            return Data.ToString();
        }

    }

二叉树构建类和4种遍历算法:

  /// <summary>
    /// 二叉树类
    /// </summary>
    public class BinaryTree
    {
        /// <summary>
        /// 头结点
        /// </summary>
        public Node Head { get; set; }

        /// <summary>
        /// 构建二叉树的字符串
        /// </summary>
        private string cStr { get; set; }

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="Str"></param>
        public BinaryTree(string Str)
        {
            this.cStr = Str;                //保存构造字符串
            this.Head = new Node(Str[0]);   //添加头结点
            //给头结点添加孩子节点
            Add(this.Head, 0);
        }

        /// <summary>
        /// 添加节点
        /// </summary>
        /// <param name="parent">父节点</param>
        /// <param name="index">父亲索引</param>
        private void Add(Node parent, int index)
        {
            int leftIndex = index * 2 + 1;    //左孩子索引
            int rightIndex = index * 2 + 2;   //右孩子索引
            if (leftIndex < cStr.Length)      //左索引没有超过字符串长度
            {
                if (cStr[leftIndex] != '#')    //# 用来表示空节点
                {
                    parent.Left = new Node(cStr[leftIndex]);
                    //继续递归调用添加左孩子节点
                    Add(parent.Left, leftIndex);
                }
            }
            if (rightIndex < cStr.Length)      //右索引没有超过字符串长度
            {
                if (cStr[rightIndex]!='#')     //# 用来表示空节点 
                {
                    parent.Right = new Node(cStr[rightIndex]);
                    //继续递归调用添加右孩子节点
                    Add(parent.Right, rightIndex);
                }
            }
        }

        /// <summary>
        /// 先序遍历
        /// </summary>
        /// <param name="node"></param>
        public void PreOrder(Node node)
        {
            if (node!=null)
            {
                Console.Write(node.ToString());
                PreOrder(node.Left);  //递归左
                PreOrder(node.Right);  //递归右
            }
        }

        /// <summary>
        /// 中序遍历
        /// </summary>
        /// <param name="node"></param>
        public void MidOrder(Node node)
        {
            if (node != null)
            {       
                MidOrder(node.Left);  //递归左
                Console.Write(node.ToString());
                MidOrder(node.Right);  //递归右
            }
        }

        /// <summary>
        /// 后序遍历
        /// </summary>
        /// <param name="node"></param>
        public void AfterOrder(Node node)
        {
            if (node != null)
            {
                AfterOrder(node.Left);  //递归左        
                AfterOrder(node.Right);  //递归右
                Console.Write(node.ToString());
            }
        }

        /// <summary>
        /// 广度优先遍历
        /// </summary>
        public void LevelOrder()
        {
            Queue<Node> queue = new Queue<Node>();    //创建队列
            queue.Enqueue(this.Head);                 //根节点入队
            while (queue.Count>0)                     //
            {
                Node node = queue.Dequeue();   //出队
                Console.Write(node.ToString());
                if (node.Left!=null)
                {
                    //将左孩子入队
                    queue.Enqueue(node.Left);
                }
                if (node.Right!=null)   
                {
                    //将右孩子入队
                    queue.Enqueue(node.Right);
                }
            }
        }


    }

调用:

{
                Console.WriteLine("-------------------二叉树遍历测试-------------------");
                string Str= "ABCDE#F";
                BinaryTree bTree = new BinaryTree(Str);

                Console.WriteLine("先序遍历:");
                bTree.PreOrder(bTree.Head);
                Console.WriteLine();
                Console.WriteLine("中序遍历:");
                bTree.MidOrder(bTree.Head);
                Console.WriteLine();
                Console.WriteLine("后序遍历:");
                bTree.AfterOrder(bTree.Head);
                Console.WriteLine();
                Console.WriteLine("广度优先遍历:");
                bTree.LevelOrder();

}

运行结果:

原文链接:第九节:树与二叉树深度剖析(一) - Yaopengfei - 博客园

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

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

相关文章

【快速幂】876. 快速幂求逆元

876. 快速幂求逆元 文章目录题目描述输入格式&#xff1a;输出格式&#xff1a;数据范围输入样例输出样例方法&#xff1a;快速幂解题思路代码复杂度分析&#xff1a;题目描述 给定 n 组 ai,pia_i,p_iai​,pi​&#xff0c;其中 pip_ipi​ 是质数&#xff0c;求 aia_iai​ 模 …

MySQL 8.0.31中使用MySQL Workbench提示配置文件错误信息

MySQL 8.0.31中使用MySQL Workbench提示配置文件错误信息 Error opening configuration file UnicodeDecodeError:‘gbk’ coded can’t decode byte 0x92 in position 5004: illegal multibyte sequence 配置文件之前安装MySQL Server的时候编码格式好像改了, 才使的MySQL W…

高级通讯录(C语言)

目录 前言 为何要实现高级通讯录 高级通讯录实现&#xff1a; 创建通讯录 打印菜单 初始化通讯录 实现加载功能 实现添加功能 实现增容功能 实现删除功能 实现查询功能 实现修改功能 实现查询所有联系人功能 实现排序功能 实现清空功能 实现保存功能 实现退出功能 通讯录总代码…

基于stm32G431RBT6蓝桥杯嵌入式—新建工程和点灯

目录 cube新建工程 GPIO输出与LED模块 推挽输出模式push-pull 开漏输出模式open-drain LED原理 程序 cube新建工程 时钟树配置&#xff1a;一般使用内部时钟。 GPIO输出与LED模块 输入0&#xff0c;输出GND&#xff1b;输入1&#xff0c;输出3.3V。 P-MOS管高电平导通&a…

2023年新手卖家怎么做好跨境电商?

随着互联网时代的高速发展&#xff0c;跨境电商成为我国经济发展中不可忽视的重要力量&#xff0c;在国内优惠政策大力扶持以及线上消费习惯的加持下&#xff0c;跨境电商行业迎来了发展的黄金机遇期。但是随之而来的是各大资本的涌入&#xff0c;跨境电商中小卖家们也面对越发…

第五章 程序控制结构

一、程序流程控制介绍 在程序中&#xff0c;程序运行的流程控制决定程序是如何执行的&#xff0c;是我们必须掌握的&#xff0c;主要有三大流程控制语句。 &#xff08;1&#xff09;顺序控制 &#xff08;2&#xff09;分支控制 &#xff08;3&#xff09;循环控制顺序控制&am…

c++11 标准模板(STL)(std::forward_list)(十二)

定义于头文件 <forward_list> template< class T, class Allocator std::allocator<T> > class forward_list;(1)(C11 起)namespace pmr { template <class T> using forward_list std::forward_list<T, std::pmr::polymorphic_…

数据库管理-第五十四期 春节俩故障(20230128)

数据库管理 2023-01-28第五十四期 春节俩故障1 19.13 bug 320763052 19.15 CSS总结第五十四期 春节俩故障 虽然春节期间除了年三十的现场值班和远程值班&#xff0c;没啥事的&#xff0c;结果还是处理了俩故障&#xff0c;今天上工&#xff0c;分析一下。 1 19.13 bug 320763…

了解3dmax面片建模方式

首先在模式里面选中面片栅格&#xff0c;Patch Grids&#xff1b; 选择四边形面片&#xff1b; 在顶视图中画一个面片&#xff0c;之后如下&#xff1b;面片从前和左看到的是一条线&#xff1b; 调整面片参数&#xff0c;长度分段和宽度分段分别为2和3&#xff1b; 工具栏选中修…

信息论复习—线性分组码的基本性质

目录 线性分组码&#xff1a; 非线性码示例&#xff1a; 线性码示例: 许用码字间的距离&#xff0d;&#xff0d;码距&#xff1a; 码距与码的检错纠错能力之间的关系&#xff1a; 线性分组码的基本性质&#xff1a; 线性分组码的最小码距与最小码重的关系&#xff1a; …

最详细的华为VRP操作指南

第二章&#xff1a;华为VRP系统 通用路由平台VRP&#xff08;Versatile Routing Platform&#xff09;是华为公司数据通信产品的通用操作系统平台。它以IP业务为核心&#xff0c;采用组件化的体系结构&#xff0c;在实现丰富功能特性的同时&#xff0c;还提供了基于应用的可裁…

Android深入系统完全讲解(43)

为什么要编码&#xff1f;这里主要是因为信息有甬余需要压缩&#xff0c;所以会出现各类算法。如果存储原始 数据&#xff0c;那就很大。 举例来说&#xff0c;BMP&#xff0c;压缩算法有 jpg&#xff0c;png 等等。 比如 PCM&#xff0c;压缩的 mp3 H.264 相关 这里我们看一下…

剑指 Offer 09. 用两个栈实现队列(力扣)

一&#xff1a;题目用两个栈实现一个队列。队列的声明如下&#xff0c;请实现它的两个函数 appendTail 和 deleteHead &#xff0c;分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素&#xff0c;deleteHead 操作返回 -1 )示例 1&#xff1a;输入&…

小米万兆路由器里的Docker安装Alist

小米2022年12月份发布了万兆路由器&#xff0c;里面可以使用Docker。 今天尝试在小米的万兆路由器里安装Alist v3.9.2。 准备工作 请参考https://engchina.blog.csdn.net/article/details/128515422的准备工作。 创建存储 在第三方管理(SimpleDocker)&#xff0c;单击"…

Scala-变量和数据类型

注释在Scala中注释和Java基本一样单行注释&#xff1a;// 多行注释&#xff1a;/* */ 文档注释&#xff1a;/****/变量和常量基本语法声明变量&#xff1a;var 变量名 [:变量类型] 初始值 如&#xff1a;var a:Int 10 声明常量&#xff1a;val 变量名 [:常量类型] 初始值 如…

深度卷积神经网络、池化层

目录1.深度卷积神经网络(a deep convolutional neural network)输入图像的维度是&#xff0c;如果&#xff0c;计算输出图像维度公式&#xff1a;。s表示步幅&#xff0c;p表示填充的层数。filters的通道数是和输入图像的通道数保持一致的。分析上图案例&#xff1a;第一层卷积…

《深入浅出计算机组成原理》学习笔记 Day13

数据通路&#xff08;中&#xff09;1. 时钟信号的硬件实现2. 通过 D 触发器实现存储功能参考1. 时钟信号的硬件实现 有些电路只要需要给定输入&#xff0c;就能得到固定的输出&#xff0c;这样的电路称为组合逻辑电路&#xff08;Combination Logic Circuit&#xff09;。 时…

如何使用自己的虚拟机(linux)做个RAID玩?

在虚拟机原有基础上添加两块磁盘&#xff08;均20G&#xff09;&#xff0c;步骤如下&#xff1a;使用命令“lsblk”检查添加是否生效&#xff1b;使用命令“mdadm”进行linux的raid管理&#xff08;先安装mdadm工具&#xff09;创建RAID1,名字为md1&#xff0c;即使用mdadm命令…

虚拟机字节码执行引擎

------摘自周志明 《深入理解Java虚拟机》运行时栈帧结构Java虚拟机以方法作为最基本的执行单元&#xff0c;“栈帧”&#xff08;Stack Frame&#xff09;则是用于支持虚拟机进行方法调用和方法执行背后的数据结构&#xff0c;它也是虚拟机运行时数据区中的虚拟机栈&#xff…

C语言及算法设计课程实验三:最简单的C程序设计——顺序程序设计((一、二、三、四)的综合文章)

C语言及算法设计课程实验三&#xff1a;最简单的C程序设计——顺序程序设计&#xff08;&#xff08;一、二、三、四&#xff09;的综合文章&#xff09;一、实验目的二、 实验内容2.1、实验内容1&#xff1a;通过下面的程序掌握各种格式转换符的正确使用方法2.2、实验内容2&am…