基于PHP简单实现BTree

背景

这几天看mysql索引的知识,了解到BTree数据结构,无聊想挑战一下去实现一下,Btree的数据结构特点我就不介绍,具体的图解请查看这个链接https://blog.youkuaiyun.com/li_canhui/article/details/85305147,我比较笨,不会使用画图软件😁

<?php
//存放每一个数据的容器
class Ceil
{
    public $index;//数据

	//仅当一个Unit的第一个Ceil需要使用左地址进行数据传递
    public $left;//左地址
    
    public $right;//右地址

    public function __construct($index)
    {
        $this->index = $index;
    }
}
//
class Unit
{
    public $parent;//父Unit地址,默认为null

    public $items;//存储Ceil的队列

    public static $fork_num;//叉值

    public function __construct($items)
    {
        $this->items = $items;
    }
	//添加ceil
    public function add_ceil($ceil){
    	//去找到具体的位置
        $position_message = $this->find_node($this,$ceil);
        $unit = $position_message['unit'];
        //向该位置添加ceil
        array_splice($unit->items,$position_message['position'],0,[$ceil]);
        //校验是否需要进行当前Unit进行分裂操作
        if(count($unit->items) == self::$fork_num){
            $this->division($unit);
        }

        return $this;
    }
    //min -> max sort 插入ceil的值与队列进行排序,返回插入ceil所需要点位置及其unit实例
    public function find_node(Unit $unit,Ceil $ceil){
        $pointer_position = null;
        $pointer_item = null;
        $items = $unit->items;
        //获取排序位置
        $pointer = $this->get_sort($items,$ceil);
        $pointer_position = $pointer['position'];
        $pointer_item = $pointer['item'];
        //返回0位置,切当前unit的第一个ceil存在左unit,则递归查找
        if($pointer_position == 0 && $pointer_item->left != null){
            $left_unit = $pointer_item->left;
            return $position_message = $this->find_node($left_unit,$ceil);
        }
        //非0位置,切当前unit前一个位置存在右unit,则递归查找
        if($pointer_position > 0 && $pointer_item->right != null){
            $right_unit = $pointer_item->right;
            return $position_message = $this->find_node($right_unit,$ceil);
        }

        return ['unit' => $unit,'position' => $pointer_position];
    }
	//获取插入ceil在当前unit中的位置
    public function get_sort($items,$ceil,$position_limit=false){
        $pointer_position = $pointer_item = false;
        if(($count = count($items)) == 1){
            $pointer_position = $items[0]->index > $ceil->index ? 0 : 1 ;
            $pointer_item = $items[0];
        }else{
            foreach($items as $position => $item){
                $pointer_position = $position;
                $pointer_item = $item;
                if($position == $count -1){
                    $pointer_position++;
                    break;
                }
                if($items[$position]->index > $ceil->index){
                    break;
                }
                if($items[$position]->index < $ceil->index && $items[$position+1]->index > $ceil->index){
                    $pointer_position++;
                    break;
                }
            }
        }
        if(!$position_limit){
            return [
                'position' => $pointer_position,
                'item' => $pointer_item
            ];
        }
        return $pointer_position;

    }
	//执行分裂操作
    public function division($unit){
    	//当前节点是根节点
        if($unit->parent == null){
            $left_ceil = array_slice($unit->items,0,(self::$fork_num-1)/2);
            $middle_ceil = array_slice($unit->items,(self::$fork_num-1)/2,1);
            $right_ceil = array_slice($unit->items,(self::$fork_num+1)/2);
            $unit->items = $middle_ceil;
            $left_unit = new Unit($left_ceil);
            $right_unit = new Unit($right_ceil);
            $middle_ceil[0]->left = $left_unit;
            //校验中值ceil是否存在右unit,是则将其挂载分裂的右unit的第一个ceil的左子节点
            if($middle_ceil[0]->right){
                $tmp = $middle_ceil[0]->right;
                $right_unit->items[0]->left = $tmp;
            }
            $middle_ceil[0]->right = $right_unit;
            $left_unit->parent = $right_unit->parent = $unit;
        }else{
        	//分裂当前节点存在父节点
            $parent = $unit->parent;
            $left_ceil = array_slice($unit->items,0,(self::$fork_num-1)/2);
            $middle_ceil = array_slice($unit->items,(self::$fork_num-1)/2,1);
            $right_ceil = array_slice($unit->items,(self::$fork_num+1)/2);
            $left_unit = new Unit($left_ceil);
            $right_unit = new Unit($right_ceil);
            $inserted_parent = $this->insert_parent($parent,$middle_ceil[0],$left_unit,$right_unit);
            //校验分裂后的父节点是否需要在此分裂
            if(count($inserted_parent->items) == Unit::$fork_num){
                $this->division($inserted_parent);
            }
        }

    }

    public function insert_parent($parent,$ceil,$left_unit,$right_unit){
        $position = $this->get_sort($parent->items,$ceil,true);
        //校验中值ceil是否存在右unit,是则将其挂载分裂的右unit的第一个ceil的左子节点
        if($ceil->right){
            $tmp = $ceil->right;
            $right_unit->items[0]->left = $tmp;
        }
        $ceil->right = $right_unit;
        //判断分裂后的节点插入父unit后是否是队列的最左侧,仅当左端分裂存在此现象
        if($position != 0){
            $parent->items[$position-1]->right = $left_unit;
        }else{
        	//左端分裂切中间值是父节点队列的最左值,则将分裂前的左右unit均挂载在中间节点
            $ceil->left = $left_unit;
            $parent->items[$position]->left = null;
        }
        $left_unit->parent = $right_unit->parent = $parent;
        array_splice($parent->items,$position,0,[$ceil]);
        return $parent;
    }
}
//
class BTree
{
    public $root;
	//初始化根节点unit
    public function __construct($root,$fork_num)
    {
        Unit::$fork_num = $fork_num;
        $this->root = $root;
    }
	//执行数据填充动作
    public function format($data){
        foreach($data as $index){
            $ceil = new Ceil($index);
            $this->root = $this->root->add_ceil($ceil);
        }
        return $this;
    }
}
$data = [2,1,7,6,11,4,8,13,10,5,17,9,16,20,3,12,14,18,19,15];
$root = new Unit([new Ceil(array_shift($data))]);
$fork_num = 5;
$b_tree_root = new BTree($root,$fork_num);
$b_tree = $b_tree_root->format($data);
var_dump($b_tree);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值