`
dieslrae
  • 浏览: 35387 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

多叉树:2-3-4树

    博客分类:
  • Tree
 
阅读更多
    平衡树多叉树,每个节点最多有4个子节点和3个数据项,2,3,4的含义是指一个节点可能含有的子节点的个数,效率比红黑树稍差.一般不允许出现重复关键字值.2-3-4树有以下特征:
    1、有一个数据项的节点总是有2个子节点(称为2-节点)
    2、有两个数据项的节点总是有3个子节点(称为3-节点)
    3、有三个数据项的节点总是有4个子节点(称为4-节点)

    简单的说,非叶节点的子节点树总是比它含有的数据项多1,叶节点可能含有一个,两个或三个数据项.空叶节点不存在.2-3-4树的规则如下:
    1、第一个子节点的关键字值小于父节点第一个数据项
    2、第二个子节点的关键字值小于父节点第二个数据项并大于第一个数据项
    3、第三个子节点的关键字值小于父节点第三个数据项并大于第二个数据项
    4、最后一个节点的关键字值大于父节点第三个数据项


插入操作:
    查找时没有碰到满节点时,插入很简单.找到合适的叶节点后,只要把新数据项插入进去就可以了.可能会涉及到在一个节点中移动一个或两个其他的数据项.如果节点已经满了,插入就变得复杂了.发生这种情况时,节点必须分裂以保持树的平衡.把要分裂节点中的数据项设为A,B,C
    1、创建一个新的节点,他是要分裂节点的兄弟节点,在要分裂节点的右边.
    2、数据项C移动到新节点中.
    3、数据项B移到要分裂节点的父节点中(如果要分裂的节点是根节点,就再创建一个新节点指             向根,并将要分裂的节点连接上去).
    4、要分裂节点的最右边的两个子节点连接到新的兄弟节上

删除操作:
    还是很蛋疼的操作,依然建议打作废标记...

最后,是关于红黑树的,虽然两种树看上去不一样,但是在数学上确实属于同构的,2-3-4树也能转化为一颗红黑树.
2-3-4树转化为红黑树规则:
    1、把每个2-节点转化为红黑树的黑色节点.
    2、把每个3-节点转化成一个子节点和一个父节点,子节点有两个自己的子节点,父节点 有另一个子节点,子节点涂成红色,父节点涂成黑色.
    3、把每个4-节点转化成一个父节点,两个子节点和四个孙子节点,子节点涂成红色,父节点涂成黑色.


tree代码:
public class Tree {
    private Node root;
    
    public Tree(){
        this.root = new Node();
    }
    
    /**
     * 查询数据项的值
     * @param key
     * @return
     */
    public Object find(int key){
        Item item = this.findItem(key);
        
        if(item != null && item.isValid()){
            return item.getValue();
        }else{
            return null;
        }
    }
    
    /**
     * 查询数据项
     * @param key
     * @return 
     */
    private Item findItem(int key){
        Node current = this.root;
        int index;
        
        while(true){
            if((index = current.findItem(key)) != -1){
                break;
            }else if(current.isLeaf()){
                return null;
            }else{
                current = this.getNextNode(current,key);
            }
        }
        
        return current.getItem(index);
    }
    
    /**
     * 插入一个新数据项
     * @param key
     * @param value 
     */
    public void insert(int key,Object value){
        Item item = this.findItem(key);
        
        if(item != null){
            item.setValid(true);
            item.setValue(value);
            return;
        }
        
        item = new Item(key, value);
        Node current = this.root;
        
        while(true){
            if(current.isFull()){
                //遇到数据项已满的节点,分裂
                this.split(current);
                //重新从上一级节点查询
                current = this.getNextNode(current.getParent(), key);
            }else if(current.isLeaf()){
                break;
            }else{
                current = this.getNextNode(current,key);
            }
        }
        
        current.insertItem(item);
    }
    
    /**
     * 删除数据项,这里还是采用最简单的打作废标记的办法
     * @param key 
     */
    public void remove(int key){
        Item item = this.findItem(key);
        
        if(item != null){
            item.setValid(false);
        }
    }
    
    /**
     * 分裂节点
     * @param node 
     */
    private void split(Node node){
        Item C = node.removeItem();
        Item B = node.removeItem();
          
        /**
         * 新建一个兄弟节点,在要分裂的节点右边,把最后一个数据项移到新节点,
         * 并把要分裂节点的右边两个子节点挂到新节点上
         */
        Node right = new Node();
        right.insertItem(C);
        right.connect(0, node.disconnect(2));
        right.connect(1, node.disconnect(3));
        
        /**
         * 将要分裂节点的中间一个数据项移动到其父节点(如果要分裂节点是根节点
         * ,就新建一个节点并指向根)
         */
        Node parent = node == this.root ? new Node() : node.getParent();
        if(node == this.root){
            parent.connect(0, node);
            this.root = parent;
        }
        
        int index = parent.insertItem(B);
        int n = parent.getItemSize();
        
        for(int i=n-1;i>index;i--){
            parent.connect(i + 1,parent.disconnect(i));
        }
        
        parent.connect(index + 1, right);
    }
    
    /**
     * 查找下一个节点
     * @param node
     * @param key
     * @return 
     */
    private Node getNextNode(Node node,int key){
        for(int i=0;i<node.getItemSize();i++){
            if(node.getItem(i).getKey() > key){
                return node.getChild(i);
            }
        }
        
        return node.getChild(node.getItemSize());
    }
}


node代码:
public class Node {
    private Item[] itemArray;
    private Node[] childArray;
    private Node parent;
    private int itemSize;
    
    public Node(){
        this.itemArray = new Item[3];
        this.childArray = new Node[4];
        this.itemSize = 0;
    }
    
    /**
     * 连接子节点
     * @param num 子节点所在位置:0,1,2,3
     * @param child 子节点
     */
    public void connect(int num,Node child){ 
        this.childArray[num] = child;
        
        if(child != null){
            child.parent = this;
        }
    }
    
    /**
     * 断开子节点连接并返回子节点
     * @param num 子节点所在位置:0,1,2,3
     * @return 断开的子节点
     */
    public Node disconnect(int num){
        Node node = this.childArray[num];
        this.childArray[num] = null;
        
        return node;
    }
    
    public Node getChild(int num){
        return this.childArray[num];
    }
    
    public boolean isLeaf(){
        return this.childArray[0] == null;
    }
    
    public boolean isFull(){
        return this.itemSize == 3;
    }
    
    /**
     * 通过key查找节点中数据项的下标
     * @param key
     * @return 下标,如果没有找到返回-1
     */
    public int findItem(int key){
        for(int i=0;i<this.itemSize;i++){
            if(this.getItem(i).getKey() == key){
                return i;
            }
        }
        
        return -1;
    }
    
    /**
     * 将新数据项插入节点并返回下标
     * @param item 数据项
     * @return 下标
     */
    public int insertItem(Item item){
        this.itemSize++;
        
        for(int i=this.itemSize-2;i>=0;i--){
            Item compare = this.getItem(i);
            
            if(compare.getKey() > item.getKey()){
                this.itemArray[i + 1] = compare;
            }else{
                this.itemArray[i + 1] = item;
                return i + 1;
            }
        }
        
        this.itemArray[0] = item;
        
        return 0;
    }
    
    /**
     * 删除并返回最后一个数据项
     * @return 删除的数据项
     */
    public Item removeItem(){
        Item item = this.itemArray[this.itemSize - 1];
        this.itemArray[this.itemSize - 1] = null;
        this.itemSize--;
        
        return item;
    }
    
    public int getItemSize(){
        return this.itemSize;
    }
    
    public Item getItem(int index){
        return this.itemArray[index];
    }
    
    public void setParent(Node parent){
        this.parent = parent;
    }
    
    public Node getParent(){
        return this.parent;
    }
}


item代码:
public class Item {
    private int key;
    private Object value;
    private boolean valid;
    
    public Item(){};
    
    public Item(int key,Object value){
        this.key = key;
        this.value = value;
        this.valid = true;
    }
    
    public int getKey() {
        return key;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }
    
    public boolean isValid() {
        return valid;
    }

    public void setValid(boolean valid) {
        this.valid = valid;
    }
}
分享到:
评论

相关推荐

    家谱图-多叉树-c语言-数据结构

    家谱图,也称为家族树或关系树,是一种特殊类型的多叉树数据结构,用于表示个人之间的亲属关系。在这个项目中,我们将深入探讨如何使用C语言实现多叉树,特别是构建家谱图。 首先,我们要理解什么是多叉树。与...

    多叉树的树形显示

    3. **遍历**:类似于二叉树,多叉树也可以进行前序、中序和后序遍历,但定义会稍微复杂些,因为子节点数量不固定。 4. **深度优先搜索(DFS)**和**广度优先搜索(BFS)**:这两种遍历方法同样适用于多叉树,DFS沿着...

    多叉树的遍历,可以打印出树形结构,也可以只打印叶节点,或打印指定层的节点(一位德国教授写的)

    2. **插入节点**:向多叉树中添加新节点时,可以指定新节点作为现有节点的子节点或者兄弟节点。例如,`node-&gt;addChild(new_node)`将`new_node`作为`node`的一个子节点插入到树中。 3. **删除节点**:通过提供一个...

    多叉树的实现

    ### 多叉树的实现详解 #### 一、概述 多叉树是一种常见的数据结构,在计算机科学领域有广泛的应用场景,比如在机器学习中的决策树、数据库索引、文件系统等。多叉树与二叉树相比,节点可以拥有两个以上的子节点。...

    基于顺序存储实现的多叉树容器

    在计算机科学中,数据结构是组织和管理数据的重要方式,多叉树作为一种非线性数据结构,具有广泛的应用。在本话题中,我们将探讨一种基于顺序存储实现的多叉树容器,它使用C++模板语言特性,实现了泛型化的设计,以...

    Python实现的多叉树寻找最短路径算法示例

    本文实例讲述了Python实现的多叉树寻找最短路径算法。分享给大家供大家参考,具体如下: 多叉树的最短路径: 思想:  传入start 和 end 两个 目标值  1 找到从根节点到目标节点的路径  2 从所在路径,寻找最近的...

    多叉树 遍历

    root = Node(1, [Node(2), Node(3, [Node(4), Node(5)])]) pre_order_traversal(root) ``` 在实际应用中,遍历多叉树的方法可能会根据具体需求进行调整。例如,在文件系统中,我们可能希望按照某种特定的顺序(如...

    多叉树Huffman算法

    具体来说,多叉树哈夫曼算法不仅能够使编码的平均长度更接近信源的熵函数值,而且还能加快解码速度,实现在文本压缩领域的应用时获得更高的压缩比,通常可达约3:1左右。 #### 二、多叉树哈夫曼算法的基本原理 ####...

    多叉树(孩子兄弟).zip,(多叉树的创建和打印)

    4. 构建树结构:通过递归或迭代的方式,根据给定的数据序列构建多叉树。 接下来是打印多叉树,常见的方法有层次遍历(level-order traversal)。层次遍历从根节点开始,逐层访问所有节点。这通常使用队列(queue)...

    网络游戏-AOE网络到多叉树结构的异构转换方法.zip

    2. **构建多叉树**:以游戏世界的中心或某一固定点为根节点,根据区域的位置和大小将其分配到不同的子节点。相邻或重叠的区域可能共享部分子树,形成一个多叉树结构。 3. **空间划分**:利用多叉树的层次结构,对...

    Java实现多叉树查找

    在Java编程中,多叉树是一种非线性的数据结构,其中每个节点可以有多个子节点。这个给定的代码实现了一个简单的多叉树结构,主要包含节点类`TreeNode`,用于构建树形结构并进行查找操作。以下是这个实现的关键知识点...

    javascript数据结构之多叉树经典操作示例【创建、添加、遍历、移除等】

    2. 创建多叉树:多叉树可以通过构造函数创建,并设置根节点。如果树为空,则根节点可以为null或自定义的空节点值。 ```javascript class MultiwayTree { constructor() { this._root = null; // 树的根节点 } } ...

    java多叉树的个性化拓展

    在Java编程中,多叉树是一种非线性的数据结构,它由节点(或称为顶点)和连接这些节点的边组成。与二叉树不同,每个节点在多叉树中可以有任意数量的子节点,而不仅仅局限于两个。在本项目中,我们不仅实现了基本的...

    基于内存多叉树的Ext JS无限级树形菜单实现方案

    ### 基于内存多叉树的Ext JS无限级树形菜单实现方案 #### 一、研究背景与意义 在当前Web应用程序开发领域,Ext JS框架因其强大的功能和丰富的组件库而受到广泛欢迎,尤其在构建复杂的用户界面时表现突出。在Ext JS...

    BST.rar_二叉树_多叉树

    在IT领域,二叉树和多叉树是数据结构中的重要组成部分,它们广泛应用于各种算法设计和程序实现中。在这个“BST.rar_二叉树_多叉树”压缩包中,我们可以推测它包含了关于二叉搜索树(Binary Search Tree, BST)的相关...

    matlab决策树id3实现多叉树树形图显示.rar

    在本资源中,"matlab决策树id3实现多叉树树形图显示.rar" 文件包提供了MATLAB环境下使用ID3算法构建并可视化多叉决策树的方法。ID3(Iterative Dichotomiser 3)是由Ross Quinlan提出的,它基于信息熵和信息增益来...

    leetcode算法题主函数如何写-Algorithm-DataStructure:算法-数据结构

    (3)多叉树:Trie、并查集。 3.抽象数据结构: 有序集合TreeSet,有序映射TreeMap,底层基于平衡树实现。 无序集合HashSet,无序映射HashMap,底层基于哈希表实现。 数组Array Array.java: 基于Java中最基础的数组,...

    无限级分销系统查自己上级、下级之多叉树实现关系速查

    ### 无限级分销系统查自己上级、下级之多叉树实现关系速查 在现代电商及社交营销领域,无限级分销系统作为一种重要的营销手段被广泛采用。它通过层级关系来构建用户网络,并据此进行商品推广与销售分成。在这样的...

    关于java树型结构

    3. **多叉树应用**: - Trie树(字典树):用于快速查找字符串,常用于自动补全功能。 - B树和B+树:常用于数据库和文件系统的索引结构,能高效地处理大数据量的存储和检索。 4. **Java集合框架中的树结构**: -...

    递归-----动态树实现递归

    递归实现这些遍历策略非常直观,因为每个节点的处理逻辑几乎相同,即调用自身来遍历左子树和右子树(对于二叉树)或所有子树(对于多叉树)。 - **前序遍历**:先访问根节点,然后递归遍历左子树,最后遍历右子树...

Global site tag (gtag.js) - Google Analytics