[Collection与数据结构] 二叉树(一):二叉树的性质与基本操作

1. 树形结构

1.1 概念1 (了解)

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

  • 有一个特殊的结点,称为根结点,根结点没有前驱结点
  • 除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、…、Tm,其中每一个集合Ti (1 <= i <= m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
  • 树是递归定义的。
    在这里插入图片描述
    在这里插入图片描述
    注意:在子树之间不可以有交集,否者就不是树形结构.
    在这里插入图片描述

1.2 概念2 (重点)

在这里插入图片描述

  • 结点的度:一个结点含有子树的个数称为该结点的度,如b的度为2.
  • 树的度:一棵树中,所有结点度的最大值称为树的度,如上面这棵树的度为2.
  • 叶子结点或终端结点:度为0的结点称为叶结点,如上图中d,g,h,i都是叶子结点.
  • 双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点,如g的父结点是e.
  • 孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点,如b的子节点是d,e.
  • 根结点:一棵树中,没有双亲结点的结点,如上面这棵树的根节点是a.
  • 结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推.
  • 树的高度或深度:树中结点的最大层次,如上面这棵树的深度是4.
  • 兄弟结点:具有相同父结点的结点互称为兄弟结点,如g的兄弟结点是h
  • 堂兄弟结点:双亲在同一层的结点互为堂兄弟,如e是f的堂兄弟结点.

1.3 树的表示形式

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法,孩子表示法、孩子双亲表示法、孩子兄弟表示法等等。我们这里就简单的了解其中最常用的孩子兄弟表示法。下图中,c代表child,b代表brother.
在这里插入图片描述

1.4 树的应用

  1. 文件管理系统,如Linux操作系统的目录
    在这里插入图片描述

2. 二叉树(重点)

2.1 概念

一棵二叉树是结点的一个有限集合,该集合:

  1. 或者为空
  2. 或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成
    在这里插入图片描述
    从上图可以看出:
  3. 二叉树不存在度大于2的结点.
  4. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树.

注意:对于任意的二叉树都是由以下几种情况复合而成的:
在这里插入图片描述

2.2 两种特殊的二叉树

  1. 满二叉树:
    一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵二叉树的层数为K,且结点总数是2k-1,则它就是满二叉树。
  2. 完全二叉树:
    通过层序遍历的方法,==从上到下,从左到右,依次存储结点,==中间不可以有断开.
    [注] 满二叉树是一棵特殊的完全二叉树.
    在这里插入图片描述
    在这里插入图片描述

2.3 二叉树的性质

  1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 2i-1(i>0)个结点.
  2. 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是2k-1 (k>=0).
  3. 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1.(做题经常用)
  4. 具有n个结点的完全二叉树的深度k为log2(n+1)向上取整.
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
    • 若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
    • 若2i+1<n,左孩子序号:2i+1,否则无左孩子
    • 若2i+2<n,右孩子序号:2i+2,否则无右孩子

2.4 二叉树的存储

在这里,我们使用类似与链表的链式存储.
二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有**二叉(找不到父节点,类似与单向列表)和三叉(可以找到父节点,类似与双向链表)**表示方式,具体如下:

// 孩子表示法
class Node {int val; // 数据域Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}// 孩子双亲表示法
class Node {int val; // 数据域Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树Node parent;    // 当前节点的父节点
}

2.5 二叉树的基本操作

前置说明:我们这里使用非常简单的方法来创建一棵二叉树,此二叉树是孩子表示法,其实真正创建二叉树的方法不是这样的,我们后边介绍.我们创建下面这棵二叉树:
在这里插入图片描述

2.5.1 二叉树的遍历

  • NLR:前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点—>根的左子树—>根的右子树
  • LNR:中序遍历(Inorder Traversal)——根的左子树—>根节点—>根的右子树
  • LRN:后序遍历(Postorder Traversal)——根的左子树—>根的右子树—>根节点
  • 层序遍历: 从上到下,从左到右,依次遍历.
public class BinaryTree  {static class Node{public int value;public Node left;public Node right;public Node(int value) {this.value = value;}}public int treeSize;/*** 创建一棵默认的树* @return*/public Node createTree(){//注意:真正创建二叉树的方法不是这这样的,我们后面介绍Node a = new Node(1);Node b = new Node(2);Node c = new Node(3);Node d = new Node(4);Node e = new Node(5);Node f = new Node(6);Node g = new Node(7);a.left = b;a.right = c;b.left = d;c.left = e;c.right = f;d.left = g;return a;}/*** 前序遍历* @param root*/public void preOrder(Node root){if (root == null){return;}System.out.print(root.value+" ");preOrder(root.left);preOrder(root.right);}/*** 中序遍历* @param root*/public void inOrder(Node root){if (root == null){return;}preOrder(root.left);System.out.print(root.value+" ");preOrder(root.right);}/*** 后序遍历* @param root*/public void postOrder(Node root){if (root == null){return;}preOrder(root.left);preOrder(root.right);System.out.print(root.value+" ");}/*** 计算树的大小* @param root* @return*/public int size(Node root) {if (root == null){return 0;}treeSize++;size(root.left);size(root.right);return treeSize;}/*** 获取树叶子结点的个数* @param root* @return*/public int getLeafNodeCount(Node root) {if (root == null){return 0;}if (root.left == null && root.right == null){return 1;}return getLeafNodeCount(root.left)+getLeafNodeCount(root.right);}/*** 获取该树的第k层有几个结点* @param root* @param k* @return*/public int getKLevelNodeCount(Node root, int k) {if (root == null){return 0;}if (k == 1){return 1;}return getKLevelNodeCount(root.left,k-1)+getKLevelNodeCount(root.right,k-1);//每递归一层,k-1//相对与根节点,第三层就是第三层,相对第二层,第三层是第二层,以此类推...}/*** 获取树的高度,取左子树和右子树的最大值+1(加上根节点所在的层)* @param root* @return*/public int getHeight(Node root) {if (root == null){return 0;}return Math.max(getHeight(root.left),getHeight(root.right))+1;}/*** 在树中寻找val值是否存在* @param root* @param val* @return*/public Node find(Node root, int val) {if (root == null){return null;}if (root.value == val){return root;}Node leftNode = find(root.left,val);if (leftNode != null){//写成判断地址的形,如果写成值的形式,可能会报空指针异常return leftNode;//如果从左树中找到,直接返回,就不用遍历右树,以此来减小时间复杂度}Node rightNode = find(root.right,val);if (rightNode != null){return rightNode;}return null;//递归到底了,说明没找到,返回null}
/*
层序遍历和判断是否为完全二叉树比较复杂,我们后续介绍*/
}

开始测试:

public class Test {public static void main(String[] args) {BinaryTree binaryTree = new BinaryTree();BinaryTree.Node root = binaryTree.createTree();binaryTree.preOrder(root);System.out.println();binaryTree.inOrder(root);System.out.println();binaryTree.postOrder(root);System.out.println();System.out.println(binaryTree.size(root));System.out.println(binaryTree.getLeafNodeCount(root));System.out.println(binaryTree.getKLevelNodeCount(root,4));System.out.println(binaryTree.getHeight(root));System.out.println(binaryTree.find(root,7));System.out.println(binaryTree.find(root,8));}
}

测试结果:
在这里插入图片描述

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

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

相关文章

使用剧本批量部署rsync服务端实战

目录 1、实战部署 编写剧本 执行剧本测试&#xff01;&#xff01;&#xff01; 2、部署方式对比 1、实战部署 编写剧本 执行剧本测试&#xff01;&#xff01;&#xff01; 2、部署方式对比 ansible模块实战-部署rsync服务端-CSDN博客 ansible临时命令和playbook区别 …

【UE5.1】使用MySQL and MariaDB Integration插件——(4)修改、插入、删除数据

目录 效果 步骤 一、修改 二、插入、删除 在上一篇博客&#xff08;【UE5.1】使用MySQL and MariaDB Integration插件——&#xff08;3&#xff09;表格形式显示数据&#xff09;基础上继续实现修改、插入和删除数据库数据的功能 效果 修改数据&#xff1a; 插入数据&…

vue 开发 滑动页面中出现tabs 并且需要分页的

效果 需求 我们这个页面顶部有tabs 栏 而且可以滑动到底部 进行分页 实现这样的页面我们应该怎么做 你应该会想到scroll-view 这个组件吧 下面我们来详情介绍一下这个页面的实现和功能开发 首先展示一下代码 item 循环项 <template><div class"wechat-or…

【数据结构与算法】贪心算法及例题

目录 贪心算法例题一&#xff1a;找零问题例题二&#xff1a;走廊搬运物品最优方案问题输入样例例题三&#xff1a;贪心自助餐 贪心算法 贪心算法是一种在每一步选择中都采取当前状态下最优的选择&#xff0c;以期望最终达到全局最优解的算法。它的核心思想是每次都选择当前最…

zabbix监控服务

一、监控软件的作用 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果和网站的健康状态 利用一个优秀的监控软件&#xff0c;我们可以&#xff1a; 对系统不间断实时监控实时反馈系统当前状态保…

2024第十五届蓝桥杯JavaB组省赛部分题目

目录 第三题 第四题 第五题 第六题 第七题 第八题 转载请声明出处&#xff0c;谢谢&#xff01; 填空题暂时可以移步另一篇文章&#xff1a;2024第十五届蓝桥杯 Java B组 填空题-CSDN博客 第三题 第四题 第五题 第六题 第七题 第八题 制作不易&#xff0c;还请点个赞支持…

【软件工程】UML用例图介绍和实例说明

文章目录 1、什么是用例图2、用例图的作用3、怎么画用例图4、三要素说明5、实例说明 1、什么是用例图 用例图&#xff08;Use Case Diagram&#xff09;是统一建模语言&#xff08;UML&#xff09;的一种图&#xff0c;它主要用于描述系统的功能和用户&#xff08;参与者&…

实现智能水控 | 基于ACM32 MCU的分体式水控方案

分体式水控概述 分体式水控是一种常见的水控系统&#xff0c;它的工作原理是通过水的流动来控制水的供应和排放&#xff0c;该系统一般由两部分组成&#xff1a;控制器和水阀。控制器负责监测水的流量和压力&#xff0c;根据设定的参数来控制水阀的开和关&#xff0c;从而实现水…

PMP一般需要提前多久备考?

很多考生在备考PMP前都会有这样问题&#xff0c;那么考取PMP需要提前多长时间备考比较合适呢&#xff1f;是两个月&#xff1f;还是三个月&#xff1f;还是四个月&#xff1f; 我觉得因人而异。像有些学霸踏踏实实备考一个半月&#xff0c;每天花个4、5个小时去学习&#xff0…

医疗设备防漏费系统安装的必要性是什么,人情、管理、增收?

医疗漏费新闻 19138173009&#xff08;刘&#xff09; 请大家稍作停留&#xff0c;聚焦网页新闻。那些隐藏在暗处的私收费、人情检查&#xff0c;正是我们这个时代需要警惕的痛点。或许&#xff0c;在网络上&#xff0c;你曾看到过类似的新闻&#xff0c;那些关于抖音上的漏费…

精益管理培训:谁需要它,为什么需要?

当下&#xff0c;精益管理作为一种先进的管理理念和方法&#xff0c;正被越来越多的企业所重视。那么&#xff0c;精益管理培训适合哪些人群呢&#xff1f;天行健精益管理培训公司解析如下&#xff1a; 一、企业中高层管理者 企业中高层管理者是企业战略决策和日常运营的核心力…

单细胞分析|映射和注释查询数据集

reference映射简介 在本文中&#xff0c;我们首先构建一个reference&#xff0c;然后演示如何利用该reference来注释新的查询数据集。生成后&#xff0c;该reference可用于通过cell类型标签传输和将查询cell投影到reference UMAP 等任务来分析其他查询数据集。值得注意的是&am…