热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

带你了解Java数据结构和算法之234树

这篇文章主要为大家介绍了Java数据结构和算法之2-3-4树,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,

1、2-3-4 树介绍

2-3-4树每个节点最多有四个字节点和三个数据项,名字中 2,3,4 的数字含义是指一个节点可能含有的子节点的个数。对于非叶节点有三种可能的情况:

①、有一个数据项的节点总是有两个子节点;

②、有二个数据项的节点总是有三个子节点;

③、有三个数据项的节点总是有四个子节点;

简而言之,非叶节点的子节点数总是比它含有的数据项多1。如果子节点个数为L,数据项个数为D,那么:L = D + 1

叶节点(上图最下面的一排)是没有子节点的,然而它可能含有一个、两个或三个数据项。空节点是不会存在的。

树结构中很重要的一点就是节点之间关键字值大小的关系。在二叉树中,所有关键字值比某个节点值小的节点都在这个节点左子节点为根的子树上;所有关键字值比某个节点值大的节点都在这个节点右子节点为根的子树上。2-3-4 树规则也是一样,并且还加上以下几点:

为了方便描述,用从0到2的数字给数据项编号,用0到3的数字给子节点编号,如下图:

①、根是child0的子树的所有子节点的关键字值小于key0;

②、根是child1的子树的所有子节点的关键字值大于key0并且小于key1;

③、根是child2的子树的所有子节点的关键字值大于key1并且小于key2;

④、根是child3的子树的所有子节点的关键字值大于key2。

简化关系如下图,由于2-3-4树中一般不允许出现重复关键值,所以不用考虑比较关键值相同的情况。

2、搜索2-3-4树

查找特定关键字值的数据项和在二叉树中的搜索类似。从根节点开始搜索,除非查找的关键字值就是根,否则选择关键字值所在的合适范围,转向那个方向,直到找到为止。

比如对于下面这幅图,我们需要查找关键字值为 64 的数据项。

首先从根节点开始,根节点只有一个数据项50,没有找到,而且因为64比50大,那么转到根节点的子节点child1。60|70|80 也没有找到,而且60<64<70,所以我们还是找该节点的child1,62|64|66,我们发现其第二个数据项正好是64,于是找到了。

3、插入

新的数据项一般要插在叶节点里,在树的最底层。如果你插入到有子节点的节点里,那么子节点的编号就要发生变化来维持树的结构,因为在2-3-4树中节点的子节点要比数据项多1。

插入操作有时比较简单,有时却很复杂。

①、当插入没有满数据项的节点时是很简单的,找到合适的位置,只需要把新数据项插入就可以了,插入可能会涉及到在一个节点中移动一个或其他两个数据项,这样在新的数据项插入后关键字值仍保持正确的顺序。如下图:

②、如果往下寻找插入位置的途中,节点已经满了,那么插入就变得复杂了。发生这种情况时,节点必须分裂,分裂能保证2-3-4树的平衡。

ps:这里讨论的是自顶向下的2-3-4树,因为是在向下找到插入点的路途中节点发生了分裂。把要分裂的数据项设为A,B,C,下面是节点分裂的情况(假设分裂的节点不是根节点):

1、节点分裂

一、创建一个新的空节点,它是要分裂节点的兄弟,在要分裂节点的右边;

二、数据项C移到新节点中;

三、数据项B移到要分裂节点的父节点中;

四、数据项A保留在原来的位置;

五、最右边的两个子节点从要分裂处断开,连到新节点上。

上图描述了节点分裂的例子,另一种描述节点分裂的说法是4-节点变成了两个 2- 节点。节点分裂是把数据向上和向右移动,从而保持了数的平衡。一般插入只需要分裂一个节点,除非插入路径上存在不止一个满节点时,这种情况就需要多重分裂。

2、根的分裂

如果一开始查找插入节点时就碰到满的根节点,那么插入过程更复杂:

①、创建新的根节点,它是要分裂节点的父节点。

②、创建第二个新的节点,它是要分裂节点的兄弟节点;

③、数据项C移到新的兄弟节点中;

④、数据项B移到新的根节点中;

⑤、数据项A保留在原来的位置;

⑥、要分裂节点最右边的两个子节点断开连接,连到新的兄弟节点中。

上图便是根分裂的情况,分裂完成之后,整个树的高度加1。另外一种描述根分裂的方法是说4-节点变成三个2-节点。

注意:插入时,碰到没有满的节点时,要继续向下寻找其子节点进行插入。如果直接插入该节点,那么还要进行子节点的增加,因为在2-3-4树中节点的子节点个数要比数据项多1;如果插入的节点满了,那么就要进行节点分裂。下图是一系列插入过程,有4个节点分裂了,两个是根,两个是叶节点:

4、完整源码实现

分为节点类Node,表示每个节点的数据项类DataItem,以及最后的2-3-4树类Tree234.class

package com.ys.tree.twothreefour;
public class Tree234 {
    private Node root = new Node() ;
    /*public Tree234(){
        root = new Node();
    }*/
    //查找关键字值
    public int find(long key){
        Node curNode = root;
        int childNumber ;
        while(true){
            if((childNumber = curNode.findItem(key))!=-1){
                return childNumber;
            }else if(curNode.isLeaf()){//节点是叶节点
                return -1;
            }else{
                curNode = getNextChild(curNode,key);
            }
        }
    }
    public Node getNextChild(Node theNode,long theValue){
        int j;
        int numItems = theNode.getNumItems();
        for(j = 0 ; j  itemIndex ; j--){
            Node temp = parent.disconnectChild(j);
            parent.connectChild(j+1, temp);
        }
        parent.connectChild(itemIndex+1, newRight);
        //处理新建的右节点
        newRight.insertItem(itemC);
        newRight.connectChild(0, child2);
        newRight.connectChild(1, child3);
    }
    //打印树节点
    public void displayTree(){
        recDisplayTree(root,0,0);
    }
    private void recDisplayTree(Node thisNode,int level,int childNumber){
        System.out.println("levle="+level+" child="+childNumber+" ");
        thisNode.displayNode();
        int numItems = thisNode.getNumItems();
        for(int j = 0; j = 0 ; j--){
                if(itemArray[j] == null){//如果为空,继续向前循环
                    continue;
                }else{
                    long itsKey = itemArray[j].dData;//保存节点某个位置的数据项
                    if(newKey 

5、2-3-4树和红黑树  

2-3-4树是多叉树,而红黑树是二叉树,看上去可能完全不同,但是,在某种意义上它们又是完全相同的,一个可以通过应用一些简单的规则变成另一个,而且使他们保持平衡的操作也是一样,数学上称他们为同构。

①、对应规则

应用如下三条规则可以将2-3-4树转化为红黑树:

一、把2-3-4树中的每个2-节点转化为红-黑树的黑色节点。

二、把每个3-节点转化为一个子节点和一个父节点,子节点有两个自己的子节点:W和X或X和Y。父节点有另一个子节点:Y或W。哪个节点变成子节点或父节点都无所谓。子节点涂成红色,父节点涂成黑色。

三、把每个4-节点转化为一个父节点和两个子节点。第一个子节点有它自己的子节点W和X;第二个子节点拥有子节点Y和Z。和前面一样,子节点涂成红色,父节点涂成黑色。

下图是一颗2-3-4树转化成对应的红-黑树。虚线环绕的子树是由3-节点和4-节点变成的。转化后符合红-黑树的规则,根节点为红色,两个红色节点不会相连,每条从根到叶节点的路径上的黑节点个数是一样的。  

②、操作等价

不仅红-黑树的结构与2-3-4树对应,而且两种树操作也一样。2-3-4树用节点分裂保持平衡,红-黑树用颜色变换和旋转保持平衡。

上图是4-节点分裂。虚线环绕的部分等价于4-节点。颜色变换之后,40,60节点都为黑色的,50节点是红色的。因此,节点 50 和它的父节点70 对于3-节点,如上图虚线所示。

6、2-3-4 树的效率

分析2-3-4树我们可以和红黑树作比较分析。红-黑树的层数(平衡二叉树)大约是log2(N+1),而2-3-4树每个节点可以最多有4个数据项,如果节点都是满的,那么高度和log4N。因此在所有节点都满的情况下,2-3-4树的高度大致是红-黑树的一半。不过他们不可能都是满的,所以2-3-4树的高度大致在log2(N+1)和log2(N+1)/2。减少2-3-4树的高度可以使它的查找时间比红-黑树的短一些。

但是另一方面,每个节点要查看的数据项就多了,这会增加查找时间。因为节点中用线性搜索来查看数据项,使得查找时间的倍数和M成正比,即每个节点数据项的平均数量。总的查找时间和M*log4N成正比。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程笔记的更多内容!


推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 数组的排序:数组本身有Arrays类中的sort()方法,这里写几种常见的排序方法。(1)冒泡排序法publicstaticvoidmain(String[]args ... [详细]
  • 面向对象之3:封装的总结及实现方法
    本文总结了面向对象中封装的概念和好处,以及在Java中如何实现封装。封装是将过程和数据用一个外壳隐藏起来,只能通过提供的接口进行访问。适当的封装可以提高程序的理解性和维护性,增强程序的安全性。在Java中,封装可以通过将属性私有化并使用权限修饰符来实现,同时可以通过方法来访问属性并加入限制条件。 ... [详细]
author-avatar
三面D夏娃所_729
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有