java数据结构 树(一)

Java数据结构之树:二叉树详解
本文介绍了树的存储结构,包括结点的概念、度、双亲与孩子结点等概念。重点讲解了二叉树的定义、性质,以及二叉树的顺序存储结构和链式存储结构。此外,还详细阐述了二叉树的遍历算法,包括先序、中序、后序和层序遍历,并分析了遍历的时间复杂度。

树的存储结构

树是n个结点的有限集。n=0时称为空树。在任意一颗非空树中:(1)有且仅有一个特定的称为根的结点;(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2……Tm,其中每一个集合本身又是一棵树,并且称为根的子树。如下图
在这里插入图片描述

1.结点: 树的结点包含一个数据元素及若干指向其子树的分支。

2.结点的度:结点拥有的子树数在这里插入代码片称为结点的度。

3.叶结点:度为0的结点称为叶结点或终端结点。

4.支节点:度不为0的结点称为非终端结点或分支结点。除根节点之外,分支结点也称为内部结点。

5.树的度:树中各结点度的最大值。如下图,结点度的最大值是D的度,所以树的度也为3
  1. 孩子结点:结点的直接后继称为该结点的孩子结点。
  2. 双亲结点:一个结点的直接前驱称为该结点的双亲结点。
  3. 兄弟结点:同一双亲结点的孩子结点之间互称兄弟结点。
  4. 祖先结点:结点的祖先结点是指从根结点到该结点路径上的所有结点。
  5. 子孙结点:一个结点的直接后继和间接后继称为该结点的子孙结点。
  6. 结点的层次:从根结点开始定义,根结点的层次为1,根的直接后继层次为2,以此类推。
  7. 堂兄弟结点:父亲是兄弟关系或堂兄关系的结点称为堂兄弟结点。
  8. 树的高度(深度):树中结点的最大层次称为树的高度或者深度。
    9.有序树:如果将树中结点的各子树看成从左到右是有次序的,不能互换的,则称该树为有序树,否则称为无序树。
  9. 森林:m颗互不相交的树的集合称为森林。将一棵非空树的根结点删去,树就变成森林;反之,给森林增加一个统一的根结点,森林就变成一棵树。

树的存储结构

双亲表示法
&nbssp这种方法用一组连续的空间存储树的结点,同时在每个结点中,附设一个指示器指示其双亲结点在表中的位置,也就是说每个结点除了保存自己的数据,还知道其双亲的位置。
由于根结点是没有双亲的,所以我们默认设置根结点的的双亲位置设置为-1.
在这里插入图片描述
这种存储方式,利用了树中每个结点(根结点除外)只有一个双亲结点的性质,如果查找结点的双亲结点,时间复杂度仅为O(1),很容易找到根结点,但是如果要找某个结点的孩子结点就需要遍历整个结构才行。
孩子表示法
这种方法通常是把每个结点的孩子结点排列起来,构成一个单链表,称为孩子链表。n个结点共有n个孩子链表(叶子结点的孩子链表为空表),而n个结点的数据和n个孩子链表的头指针组成一个顺序表。
在这里插入图片描述
孩子链表中的结点结构,child为该结点在数组中的下标,next指向下一个孩子结点。在这里插入图片描述
数组中的结点结构,data为数据,firstchild指向孩子链表的第一个结点
在这里插入图片描述
这样的结构对于我们要查找某个结点的孩子或者兄弟比较方便,只需要查找该结点的孩子链表即可,可是如果要查找某个结点的父亲,那就比较麻烦,需要遍历整个结构。那么我们可不可以把双亲表示法和孩子表示法结合一下呢?当然可以在这里插入图片描述
孩子兄弟表示法
这种方法是每个结点设置两个指针域,指向该结点的第一个孩子结点和右兄弟结点,这种表示法又被称为树的二叉表示法,或者二叉链表表示法。
在这里插入图片描述

二叉树

二叉树是一种简单而重要的树结构,任何树都可以转化为二叉树进行处理,二叉树非常适合计算机处理。

二叉树是n个结点的有限集合,该集合为空时被称为空二叉树,二叉树每个结点的度数都不能大于2,每个结点的孩子结点次序不能任意颠倒。
二叉树每个结点最多有两棵子树,没有子树或者一棵都可以,左子树和右子树是有顺序的,次序不能任意颠倒,即使树中某结点只有一棵子树,也要区分左右在这里插入图片描述
特殊二叉树

斜树:所有的结点都只有右子树的二叉树被称为右斜树,所有的结点都只有左子树的二叉树称为左斜树。两者统称为斜树。

满二叉树:一颗二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,这样的二叉树称为满二叉树。 满二叉树的顺序表示,从二叉树的根开始,从上到下从左到右从1顺序编号。在这里插入图片描述
完全二叉树:对一棵具有n个结点的二叉树编号,如果编号为i的结点和同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则称为完全二叉树。满二叉树肯定为完全二叉树,完全二叉树不一定是满二叉树。
这里写图片描述
在这里插入图片描述
二叉树的性质

性质一:在二叉树的第i层上最多有2^(i-1)个结点(i >=1)

性质二:深度为k的二叉树至多有2^k-1个结点(k>=1)

性质三:对任意一棵二叉树T,若终端结点数为n0,而其度数为2的结点数为n2,则n0=n2+1.终端结点数其实就是叶子结点数

性质四:具有n个结点的完全二叉树的深度为[log2n]+1([x]符号表示不大于x的最大整数)

性质五: 对于具有n个结点的完全二叉树,如果按照从上到下从左到右的顺序对二叉树中的所有结点从1开始进行顺序编号,则对于任意的序号为i的结点有:
(1)如i=1,则序号为i的结点是根结点,无双亲结点;如i>1,则序号为i的结点的双亲结点为 [i/2]
(2)如2i>n,则序号为i的结点无左孩子;如2i<=n,则序号为i的结点的左孩子结点的序号为2i。
(3)如2i+1>n,则序号为i的结点无右孩子;如2i+1<=n,则序号为i的结点的右孩子结点的序号为2i+1.

二叉树的顺序存储结构
二叉树的顺序存储结构就是用数组来存储二叉树中的结点,并且数组的下标要体现结点之间的存储关系。

这种存储方式对完全二叉树而言是非常方便的,因为完全二叉树的定义比较严格,我们来看一下。
在这里插入图片描述
在这里插入图片描述
如果是一般的二叉树,我们需要将不存在的结点设置为”^”,将其补成一颗完全二叉树来看待。如下图:颜色浅的是不存在的。

这里写图片描述

顺序结构对一般的二叉树而言,空间浪费太大,所以一般只用于完全二叉树。
在这里插入图片描述
二叉树的链式存储结构

对于任意二叉树来说,每个结点最多有两个孩子,所以结点的结构应该设计数据域、左孩子域、右孩子域。
在这里插入图片描述
在这里插入图片描述

二叉树的遍历
二叉树的遍历是指按照某种规律对二叉树中的每个结点进行访问且仅访问一次。访问可指计算二叉树中的数据信息,打印该结点信息,包括对结点进行任何其他操作。

二叉树遍历方式:
先序遍历

规则是若二叉树为空,则空操作返回。否则先访问根节点,然后先序遍历左子树,再先序遍历右子树。如下图:遍历顺序为ABDGHCEIF
在这里插入图片描述

中序遍历

规则是若二叉树为空,则空操作返回。否则先中序遍历根节点的左子树,然后访问根节点,然后中序遍历右子树。如下图,遍历顺序为GDHBAEICF
在这里插入图片描述

后序遍历
规则是若二叉树为空,则空操作返回。否则后序遍历左子树,然后后续遍历右子树,然后访问根节点。如下图,遍历顺序为GHDBIEFCA

在这里插入图片描述
层序遍历
规则是若二叉树为空,则空操作返回。否则从树的第一层,即根节点开始,从上到下从左到右逐层遍历。如下图,遍历顺序为:ABCDEFGHI
在这里插入图片描述

二叉树的遍历算法

二叉树的遍历操作可以采用递归的方式来运行,极其简洁。

先序遍历算法

        if(root!=null){ 
            //访问根节点
            System.out.println(root.data);
            //先序遍历左子树
            preOrder(root.leftNode);
            //先序遍历右子树
            preOrder(root.rightNode);   
        }
}

中序遍历算法

void  inOrder(TreeNode root){
        if(root!=null){ 
            //中序遍历左子树
            inOrder(root.leftNode);
            //访问根节点
            System.out.println(root.data);
            //中序遍历右子树
            inOrder(root.rightNode);    
        }
}

后序遍历算法

void  postOrder(TreeNode root){
        if(root!=null){ 
            //后序遍历左子树
            postOrder(root.leftNode);
            //后序遍历右子树
            postOrder(root.rightNode);  
            //访问根节点
            System.out.println(root.data);
        }
}

递归遍历算法的时间复杂度分析:设二叉树有n个结点,对每个结点都要执行访问和后退操作,对结点的访问为n次,所以时间复杂度为O(n)。

基于栈的递归消除,我们可以不用递归的方式进行遍历

$(function(){ $.fn.extend({ SimpleTree:function(options){ //初始化参数 var option = $.extend({ click:function(a){ } },options); option.tree=this; /* 在参数对象中添加对当前菜单的引用,以便在对象中使用该菜单 */ option._init=function(){ /* * 初始化菜单展开状态,以及分叉节点的样式 */ this.tree.find("ul ul").hide(); /* 隐藏所有子级菜单 */ this.tree.find("ul ul").prev("li").removeClass("open"); /* 移除所有子级菜单父节点的 open 样式 */ this.tree.find("ul ul[show='true']").show(); /* 显示 show 属性为 true 的子级菜单 */ this.tree.find("ul ul[show='true']").prev("li").addClass("open"); /* 添加 show 属性为 true 的子级菜单父节点的 open 样式 */ }/* option._init() End */ /* 设置所有超链接不响应单击事件 */ this.find("a").click(function(){ $(this).parent("li").click(); return false; }); /* 菜单项 接受单击 */ this.find("li").click(function(){ /* * 当单击菜单项 * 1.触发用户自定义的单击事件,将该 标签中的第个超链接做为参数传递过去 * 2.修改当前菜单项所属的子菜单的显示状态(如果等于 true 将其设置为 false,否则将其设置为 true) * 3.重新初始化菜单 */ option.click($(this).find("a")[0]); /* 触发单击 */ /* * 如果当前节点下面包含子菜单,并且其 show 属性的值为 true,则修改其 show 属性为 false * 否则修改其 show 属性为 true */ /* if($(this).next("ul").attr("show")=="true"){ $(this).next("ul").attr("show","false"); }else{ $(this).next("ul").attr("show","true"); }*/ /* 初始化菜单 */ option._init(); }); /* 设置所有父节点样式 */ this.find("ul").prev("li").addClass("folder"); /* 设置节点“是否包含子节点”属性 */ this.find("li").find("a").attr("hasChild",false); this.find("ul").prev("li").find("a").attr("hasChild",true); /* 初始化菜单 */ option._init(); }/* SimpleTree Function End */ }); });
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值