资讯专栏INFORMATION COLUMN

2-3-4树

Me_Kun / 1512人阅读

摘要:因为是大于,因此放右子树的结点中。第二层右子树会变成,,第三层的,再分裂,将加到最右边。合并子树的根与上层结点决定哪个子树要添加如果待插入的节点不是节点,那么直接在该节点插入

function Node(key, parent){
   this.parent = null;
   this.keys = [key];
   this.children = []
}

插入2

if(!this.root){
   this.root = new Node(2)
}

插入3

var node = this.search(3);
node.keys.push(3)
node.keys.sort()

插入1


注意这里会排序

var node = this.search(3);
node.keys.push(3)
node.keys.sort()

插入4

var keys = node.keys
if(keys.length === 3){
    var top = new Node(keys[1])
    var left = new Node(keys[0], top);
    var right = new Node(keys[2], top);
    top.children.push(left, right)
    var add = key < keys[1] ? left: right;
    add.keys.push(key)
    add.keys.sort()
}

这时节点已经满4个key,为4结点(实际上4始终没有放进去这个节点中),需要进行分裂,首先,根据原来的3结点,取得中间值2,新生成一个Node,将剩下的两个key,成为它的左右子树,然后4插入到右子树中。因为4是大于2,因此放右子树的结点中。

插入5

这时找到右子树,成为3结点,key为[3,4,5]

var parent = node.parent;
pushToParent(parent, keys[1])
var children = parent.children;
var index = children.indexOf(node)
var left = new Node(keys[0],parent)
var right = new Node(keys[1],parent)
children.splice(index,1, left, right)

插入6

这次也放右边,但是已经满了,需要将4,放到其父亲,变成 2,4. 然后当前结点分裂成2个, 现在有3个孩子,6放到最右边。

插入7

插入8

与上面一样,没有惊喜

插入9

插入10

这时,[7,8,9]已经满了,需要将8放上去,这时发现,[2,4,6]也满了,只好将4抽出来,变成新的根。第二层右子树会变成[6,8],第三层的[7,9]再分裂,将10加到最右边。

因此我们需要修改putKeyToParent方法,如果返回两个节点,那么它们就会平分孩子。

插入11

插入12

插入13

插入14

完整的代码如下:

   class Node {
      constructor(key, parent) {
        this.keys = [key]
        this.children = []
        this.parent = parent
      }
      isLeaf(){
          return !this.children[0]
      }
      addKeys(key){
          this.keys.push(key)
          this.keys.sort(function(a, b){
              return a - b
          })
      }
    }
    class Tree234{
        constructor(){
            this.root = null
        }
        search(node, key){
            if(node.isLeaf()){
                return node
            }
            var keys = node.keys
            for(var i = 0, n = keys.length; i < n; i++){
                if(key < keys[i]){
                   return this.search(node.children[i], key)
                }
            }
            return this.search(node.children[i], key)
        }
        insert(key){
            if(!this.root){//没有根节点
                this.root = new Node(key)
            }else{
                var node = this.search(this.root, key)
                insertNode(node, key, this)
            }
        }
     
    }
    function insertNode(node, key, tree){
        var keys = node.keys;
        if( keys.length === 3){
            var middle = keys[1], parent = node.parent, p
            //步骤1,确认新的父节点
            if(!parent){ 
                p = tree.root = new Node(middle)
                p.children = [node] //用于步骥2
            }else{
                p = insertNode(parent, middle, tree)
            }
             //步骤2,将目标节点拆成两个节点,它们的key为原keys[0], keys[1]
            var children = p.children
            var left = new Node(keys[0],p)
            var right = new Node(keys[2],p)
            children.splice(children.indexOf(node),1, left, right)//原位置替换
            //步骤3 将目标节点的children均匀分成 新生成的节点(只有在4结点的情况才这样做)
            if(node.children.length === 4){
                node.children[0].parent = left
                node.children[1].parent = left
                left.children = [ node.children[0], node.children[1]]
                node.children[2].parent = right
                node.children[3].parent = right
                right.children = [ node.children[2], node.children[3]]
            }
            //步骤4,添加新key
            var target = key < keys[0] ? left : right
            target.addKeys(key)
            return target
        }else{
            node.addKeys(key)
            return node
        }
    }
    var t = new Tree234()
    t.insert(2)
    t.insert(3)
    t.insert(1)
    t.insert(4)
    t.insert(5)
    t.insert(6)
    t.insert(7)
    t.insert(8)
    t.insert(9)
   
    t.insert(10)
    t.insert(11)
    t.insert(12)
    t.insert(13)
    t.insert(14)
    console.log(t)

另一个思路,碰到4结点就先拆成三个2结点,然后让这个子树的根与上面的结点进行合并。

      class Node {
            constructor(key, parent) {
                this.keys = [key]
                this.children = []
                this.parent = parent
            }
            isLeaf() {
                return !this.children[0]
            }
            addKeys(key) {
                //(1)如果2-3-4树中已存在当前插入的key,则插入失败,
                //否则最终一定是在叶子节点中进行插入操作
                if (!this.keys.includes(key)) {
                    this.keys.push(key)
                    this.keys.sort(function (a, b) {
                        return a - b
                    })
                }

            }
        }
        class Tree234 {
            constructor() {
                this.root = null
            }
            search(node, key) {
                if (node.isLeaf()) {
                    return node
                }
                var keys = node.keys
                for (var i = 0, n = keys.length; i < n; i++) {
                    if (key < keys[i]) {
                        return this.search(node.children[i], key)
                    }
                }
                return this.search(node.children[i], key)
            }
            insert(key) {
                if (!this.root) {//没有根节点
                    this.root = new Node(key)
                } else {
                    var node = this.search(this.root, key)
                    insertNode(node, key, this)
                }
            }
        }

        function split(keys) { //将4结点的三个key分裂成三个2结吉
            var middle = keys[1]
            var top = new Node(middle)//一个临时结点
            var left = new Node(keys[0], top)
            var right = new Node(keys[2], top)
            return [top, left, right]
        }
        function insertNode(node, key, tree) {
            var keys = node.keys;
            if (keys.length === 3) {
                var [top, left, right] = split(keys)
                top.children = [left, right]
                var parent = node.parent
                //(3)如果待插入的节点是个4节点,那么应该先分裂该节点然后再插入。
                //一个4节点可以分裂成一个根节点和两个子节点(这三个节点各含一个key)
                if (node.children.length === 4) {//是4节点
                    node.children[0].parent = left
                    node.children[1].parent = left
                    left.children = [node.children[0], node.children[1]]
                    node.children[2].parent = right
                    node.children[3].parent = right
                    right.children = [node.children[2], node.children[3]]
                }
                if (!parent) {
                    tree.root = top
                } else {
                    //我们把分裂形成的根节点中的key看成向上层插入的key,然后重复第2步和第3步。
                    var newParent = insertNode(parent, top.keys[0], tree)
                    left.parent = newParent
                    right.parent = newParent
                    //合并子树的根(top)与上层结点(node)
                    var index = newParent.children.indexOf(node)
                    newParent.children.splice(index, 1, left, right)
                }
                //决定哪个子树要添加key
                node = key < keys[0] ? left : right
            }
            //(2)如果待插入的节点不是4节点,那么直接在该节点插入
            node.addKeys(key)
            return node

        }
        var t = new Tree234()
        t.insert(2)
        t.insert(3)
        t.insert(1)
        t.insert(4)
        t.insert(5)
        t.insert(6)
        t.insert(7)

        t.insert(8)
        t.insert(9)

        t.insert(10)
        t.insert(11)
        t.insert(12)
        t.insert(13)
        t.insert(14)
        console.log(t)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90600.html

相关文章

  • 数据结构与算法(十四)深入理解红黑和JDK TreeMap和TreeSet源码分析

    摘要:很多文章或书籍在介绍红黑树的时候直接上来就是红黑树的个基本性质插入删除操作等。这也不奇怪,算法的作者就是红黑树的作者之一。所以,理解树对掌握红黑树是至关重要的。 本文主要包括以下内容: 什么是2-3树 2-3树的插入操作 红黑树与2-3树的等价关系 《算法4》和《算法导论》上关于红黑树的差异 红黑树的5条基本性质的分析 红黑树与2-3-4树的等价关系 红黑树的插入、删除操作 JDK ...

    curlyCheng 评论0 收藏0
  • 【LeetCode 二叉专项】把二叉搜索转换为累加(538)

    摘要:解法一中序遍历分析由于给定了二叉搜索树,因此首先考虑中序遍历,使用示例,我们先来分别看一下二叉搜索树和累加树中序遍历的结果二叉搜索树二叉累加树。这里还是使用示例,我们再来观察一下二叉搜索树和累加树中序遍历的结果二叉搜索树二叉累加树。 ...

    xcold 评论0 收藏0
  • 及其外部存储

    摘要:切记,红黑树在旋转和颜色变换的过程中,必须遵守红黑树的几条规则。树的外部存储磁盘布局计算机中的机械磁盘是由磁头和圆盘组成,每个圆盘上划分为多个磁道,每个磁道又划分为多个扇区。 术语 showImg(https://segmentfault.com/img/bVbai3r?w=643&h=407); 根     树最顶端的节点称为根,一棵树只有一个根 父节点     每个节...

    _Dreams 评论0 收藏0
  • 算法 | 遍历二分搜索

    摘要:又是来自我的好朋友的投稿,以下是原文基本定义二分搜索树的每个子节点最多有两个叶子节点二分搜索树的每个节点最多有一个根节点存储的元素必须具有可比较性二分搜索树每个子节点的值大于其左子节的所有节点的值小于其右子节点的所有节点的值二分搜索树不一定 showImg(https://segmentfault.com/img/remote/1460000020110337?w=1240&h=827...

    vvpvvp 评论0 收藏0
  • Map集合、散列表、红黑介绍

    摘要:并且,我们的底层都是红黑树来实现的。红黑树是一种平衡二叉树因此它没有节点。 前言 声明,本文用得是jdk1.8 前面已经讲了Collection的总览和剖析List集合: Collection总览 List集合就这么简单【源码剖析】 原本我是打算继续将Collection下的Set集合的,结果看了源码发现:Set集合实际上就是HashMap来构建的! showImg(https:/...

    2json 评论0 收藏0
  • 我理解的数据结构(五)—— 二分搜索(Binary Search Tree)

    摘要:我理解的数据结构五二分搜索树一二叉树和链表一样,动态数据结构具有唯一根节点每个节点最多有两个子节点每个节点最多有一个父节点具有天然的递归结构每个节点的左子树也是二叉树每个节点的右子树也是二叉树一个节点或者空也是二叉树二二分搜索树是二叉树每个 我理解的数据结构(五)—— 二分搜索树(Binary Search Tree) 一、二叉树 和链表一样,动态数据结构 具有唯一根节点 每个节点最...

    xeblog 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<