【数据结构】【算法】二叉树、二叉排序树、树的相关操作

您所在的位置:网站首页 二叉排序树的复杂度 【数据结构】【算法】二叉树、二叉排序树、树的相关操作

【数据结构】【算法】二叉树、二叉排序树、树的相关操作

2024-07-13 15:01:27| 来源: 网络整理| 查看: 265

树结构是以分支关系定义的一种层次结构,应用树结构组织起来的数据,逻辑上都具有明显的层次关系。 操作系统中的文件管理系统、网络系统中的域名管理、数据库系统中的索引管理等都使用了树结构来组织和管理数据。

树的基本概念

树Tree是由n个节点组成的有限集合。在任意一颗非空树中:

有且仅有一个根Root节点。当n>1时,其余节点分别为m个互不相交的有限集。其余每一个集合都是一棵树,被称为子树。

【数据结构】【算法】二叉树、二叉排序树、树的相关操作_算法

每个圆圈表示树的一个节点,其中节点A被称为树的根节点。

每一棵子树本身也是树。

常见术语节点的度Degree:节点拥有的子树数量被称为该节点的度。如上图:A的度为3,B的度为2,D的度为1,C的度为0。树的度:树中各节点的度的最大值被称为树的度。如上图:树的度为3。叶子节点Leaf:度为0的节点被称为树的叶子节点。如上图:E、F、C、G。孩子节点Child:一个节点的子树的根节点被称为该节点的孩子节点。如上图:A的孩子节点有B、C、D。双亲结点Parent:双亲节点与孩子节点对应。如上图:A为B、C、D的双亲节点。兄弟节点Sibling:一个双亲节点的孩子节点互为兄弟节点。如上图:B、C、D互为兄弟节点。节点的层次Level:从根节点开始,根节点在第1层,根节点的孩子节点为第2层,以此类推。如上图:E、F、G位于树的第3层。树的深度Depth:书中节点的最大深度被称为树的深度。如上图,树的深度为3。森林Forest:m棵互不相交的树的集合被称为森林。树的性质非空树的节点总数,等于树中所有节点的度的和+1。度为k的非空树的第i层最多有【数据结构】【算法】二叉树、二叉排序树、树的相关操作_java_02个节点。深度为h的k叉树最多有【数据结构】【算法】二叉树、二叉排序树、树的相关操作_java_03个节点。具有n个节点的k叉树的最小深度为【数据结构】【算法】二叉树、二叉排序树、树的相关操作_数据结构_04二叉树

二叉树是一种特殊的树结构,前面所说的树的特性及术语同样适用于二叉树。

二叉树的定义

二叉树Binary Tree或者为空,或者由一个根节点加上根节点的左子树和右子树组成。

要求左子树和右子树互不相交,且同为二叉树。

显然,这个定义式是递归形式的。

【数据结构】【算法】二叉树、二叉排序树、树的相关操作_java_05

二叉树的每个节点至多有两个孩子节点,其中左边的孩子节点被称为左孩子,右边的孩子节点被称为右孩子。

满二叉树与完全二叉树

如果二叉树内的任意节点都是叶子节点或有两棵树,同时叶子节点都位于二叉树的底层,则其被称为满二叉树。 若二叉树最多只有最下面两层节点的度小于2,并且底层节点(叶子节点)依次排列在该层最左边的位置上,则其被称为完全二叉树。

如图:

二叉树非常特殊,需要我们掌握它的一些性质:

在二叉树的第i层至多有【数据结构】【算法】二叉树、二叉排序树、树的相关操作_二叉树_06个节点。深度为k的二叉树至多有【数据结构】【算法】二叉树、二叉排序树、树的相关操作_图论_07个节点。对于任何一棵二叉树,如果叶子节点数为n,度为2的节点数为m,则n=m+1。具有n个节点的完全二叉树的深度为【数据结构】【算法】二叉树、二叉排序树、树的相关操作_二叉树_08二叉树的存储形式

二叉树一般采用多重链表的形式存储,直观地讲就是将二叉树的各个节点用链表节点的形式连接在一起。 每个节点都包含三个域:

一个数据域用来存放节点数据。两个指针域分别指向左右孩子节点。当没有孩子节点时,相应的指针域将被置为null。

二叉树的节点定义如下:

class BinaryTreeNode { int data; BinaryTreeNode leftChild; BinaryTreeNode rightChild; public BinaryTreeNode(int data) { this.data = data; leftChild = null; rightChild = null; } }

对于一棵二叉树而言,只要得到了它的根节点指针(引用),就可以通过链表结构访问到二叉树中的每一个节点。 所以在定义二叉树类时,通常只需要保存二叉树的根节点,而不需要记录二叉树中每一个节点的信息。

二叉树的遍历

二叉树的遍历Traversing Binary Tree就是通过某种方法将二叉树中的每个节点都访问一次,并且只访问一次。 在实际应用中,经常需要按照一定的顺序逐一访问二叉树中的节点,并对其中的某些节点进行处理。 二叉树的遍历操作是二叉树应用的基础。 二叉树的队列分为两种:

一种是基于深度优先搜索的遍历。它是利用二叉树结构的递归特性设计的,包括:先序遍历、中序遍历、后序遍历。一种是按照层次遍历二叉树,它是利用二叉树的层次结构,并借助队列实现的。

基于深度优先搜索的遍历 访问根节点的函数visit()可依据实际情况自行定义,它可以是读取节点中数据的操作,也可以是其他复杂的操作。

基于深度优先搜索的遍历算法一般有三种:先序遍历DLR、中序遍历LDR和后序遍历LRD。 其中D表示根节点,L表示左子树,R表示右子树。 因为二叉树本身就是递归结构,所以这3种遍历算法也都是以递归形式定义的。 先序遍历DLR:

访问根节点先序遍历根节点的左子树先序遍历根节点的右子树public void PreOrderTraverse() { // 先序遍历 PreOrderTraverse(root); } public void PreOrderTraverse(BinaryTreeNode root) { if (root != null) { visit(root); // 访问根结点 PreOrderTraverse(root.leftChild); // 先序遍历根结点的左子树 PreOrderTraverse(root.rightChild); // 先序遍历根结点的右子树 } }

中序遍历LDR:

中序遍历根节点的左子树访问根节点中序遍历根节点的右子树public void InOrderTraverse() { // 中序遍历 InOrderTraverse(root); } public void InOrderTraverse(BinaryTreeNode root) { if (root != null) { InOrderTraverse(root.leftChild); visit(root); InOrderTraverse(root.rightChild); } }

后序遍历LRD:

后序遍历根节点的左子树后序遍历根节点的右子树访问根节点public void PostOrderTraverse() { // 后序遍历 PostOrderTraverse(root); } public void PostOrderTraverse(BinaryTreeNode root) { if (root != null) { PostOrderTraverse(root.leftChild); PostOrderTraverse(root.rightChild); visit(root); } }

按层次遍历

按层次遍历二叉树是一种基于广度优先搜索思想的二叉树遍历算法。按层次遍历二叉树针对二叉树的每一层进行。 若二叉树非空,则先访问二叉树的第1层节点,再依次访问第2层、第3层节点,直到遍历完二叉树底层节点。 对二叉树中每一层节点的访问都按照从左到右的顺序进行。 在遍历时,需要一个队列作为辅助工具,具体步骤如下:

将二叉树的根结点指针入队列。将队首的指针元素出队列并利用这个指针访问该节点。依次将该节点的左孩子和右孩子的指针入队列。重复2操作,直到队列为空。public void LayerOrderTraverse() // 按层次遍历 { Queue queue = new LinkedList(); queue.offer(root); // 将根结点入队列 while (!queue.isEmpty()) { BinaryTreeNode node = queue.poll(); // 取出队头结点 visit(node); // 访问该结点 if (node.leftChild != null) { queue.offer(node.leftChild); // 将左孩子入队列 } if (node.rightChild != null) { queue.offer(node.rightChild); // 将右孩子入队列 } } }

方便起见,该队列是用Java容器类LinkedList的泛型实现,这样可以指定队列中的每个数据元素都是BinaryTreeNode类型的。当然也可以使用之前创建的MyQueue类实现这个辅助队列,但在使用之前需要对Myqueue类加以改造,使其每个队列节点的数据域都是BinaryTreeNode类型的。

创建二叉树

前面介绍了二叉树的遍历方法。但这需要存在一棵二叉树为基础。 最常见的创建二叉树的方式就是利用二叉树遍历的思想,在遍历过程中生成二叉树的节点,进而建立起整棵二叉树。

public void CreateBinaryTree(LinkedList nodeList) { root = CreateBiTree(nodeList); // 创建二叉树,将根结点赋值给root } public BinaryTreeNode CreateBiTree(LinkedList nodeList) { BinaryTreeNode node = null; if (nodeList == null || nodeList.isEmpty()) { return null; } Integer data = nodeList.removeFirst(); // 创建根结点 if (data != null) { node = new BinaryTreeNode(data); node.leftChild = CreateBiTree(nodeList); // 创建左子树 node.rightChild = CreateBiTree(nodeList); // 创建右子树 } return node; }

函数CreateBiTree()可以按照先序序列创建一棵二叉树。该函数的参数是一个LinkedList类型的对象nodeList。保存了每个节点的元素,用来初始化二叉树节点。 这些元素在nodeList中以先序序列的方式排列并指定了该二叉树双亲结点与孩子节点之间的关系。 函数CreateBiTree()先从nodeList中取出第一个元素,如果该元素不为null,则创建一个BinaryTreeNode类型的节点node,并将取出的元素作为node中的数据元素。 再递归地创建以node节点为根节点的二叉树的左子树和右子树。如果从**nodelist**中取出的第1个元素为**null**,则说明本层递归调用中要创建的二叉树(或二叉树的某棵子树)为空,直接返回**null**即可。 在上面的代码中,将创建二叉树的函数CreateBiTree()以及二叉树遍历函数PreOrderTraverse()、InOrderTraverse()、PostOrderTraverse()都进行了封装,使得它们对外提供的接口可以不以二叉树的根节点作为返回值或参数。这样二叉树根节点作为私有成员被隐藏起来,代码结构也更加符合面向对象的程序设计思想。

二叉排序树和AVL树

二叉排序树也称二叉查找树,是一种特殊形式的二叉树:

若它的左子树不为空,则左子树上所有节点的值均小于根节点的值。若它的右子树不为空,则右子树上所有节点的值均大于根节点的值。二叉排序树的左右子树都是二叉排序树。查找与插入节点

【数据结构】【算法】二叉树、二叉排序树、树的相关操作_二叉树_09

在二叉排序树中查找元素时,首先将给定的关键字与根节点的关键字比较,若相等则查找成功,否则将根据给定的关键字与根节点的关键字之间的大小关系,在左子树或右子树中继续查找。

在二叉树中搜索key=11,搜索步骤如下:

将key与根节点关键字10比较,因为11>10,所以在该节点的右子树中继续搜索。将key与关键字20比较,因为11 value1 && curNode.data > value2 && curNode.leftChild.data != value1 && curNode.leftChild.data != value2) { // 当前结点的值同时大于value1和value2, // 且不是value1和value2的父结点 curNode = curNode.leftChild; } else if (curNode.data < value1 && curNode.data < value2 && curNode.rightChild.data != value1 && curNode.rightChild.data != value2) { // 当前结点的值同时小于value1和value2, // 且不是value1和value2的父结点*/ curNode = curNode.rightChild; } else { return curNode.data; // 找到最低公共祖先 } } return -1; }

上面的代码中,并没有使用递归,而是通过不断修正while循环,来获取符合条件的最低公共祖先。while中第一个if的判断条件为curNode.data > value1 && curNode.data > value2 && curNode.leftChild.data != value1 && curNode.leftChild.data != value2

curNode.data > value1 && curNode.data > value2urNode.leftChild.data != value1 && curNode.leftChild.data != value2

对于第一点,也就是前两句if条件。执行的语句是修改curNode指向的对象,也就是修改while循环中下一轮循环的方向。如果判断节点介于value1和value2之间,则直接返回。 对于第二点,也就是后两句if条件。最低公共祖先一定是value1和value2节点的祖先。当判断节点为其中一个节点的双亲节点时,可以直接返回该节点。

该算法适用的前提是value1和value2在二叉排序树中真实存在。 该算法的核心思想是在二叉排序树中寻找value1和value2之间的值,如果value1或value2在二叉排序树中不存在,那么得到的结果是没有意义的。 更加完善的做法是预先判断value1和value2是否在二叉排序树中。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭