iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >关于B+tree (附python 模
  • 360
分享到

关于B+tree (附python 模

treepython 2023-01-31 07:01:54 360人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

前几天我写了点btree的东西(Http://thuhak.blog.51cto.com/2891595/1261783),今天继续这个思路,继续写b+tree。而且b+tree才是我的目的,更加深入理解文件和数据库索引的基本原理。在之前,

前几天我写了点btree的东西(Http://thuhak.blog.51cto.com/2891595/1261783),今天继续这个思路,继续写b+tree。

而且b+tree才是我的目的,更加深入理解文件和数据库索引的基本原理。


在之前,我一直只把b+tree当成是btree的一种变形,或者说是在某种情况下的一种优化,另外一些情况可能还是btree好些。但是做完之后才发现,b+tree在各种情况都可以完全取代btree,并能够让索引性能得到比btree更好的优化。因为b+tree设计的核心要点,是为了弥补btree最大的缺陷。


btree最大的缺陷是什么?

      首先,我们知道对于btree和b+tree这种多路搜索树来说,一个很重要的特点就是树的度数非常大。因为只有这样才能够降低树的深度,减少磁盘读取的次数。而树的度数越大,叶子节点在树中的比例就越大。假设度数为1000,那么叶子节点比他上一层内部节点的数量至少要多1000倍,在上一层就更加可以忽略不计了。可以说树种99.9%的节点都是叶子节点。 但是对于btree来说,所有节点都是一样的结构,都含有一定数量的数据和指向节点的指针。这两项数据占据btree节点的几乎全部的空间。一个节点内的数据的数量比硬盘指针的数量少一,可以说和指针的数量几乎相等。对于python这种动态类型语言感觉不出来,但是对于C这种固定类型语言来说,即使这个children list数组为空,这个数组的空间也都是预留出去的。导致的结果就是占绝大多数的叶子节点的children list指针数组所占的磁盘空间完全浪费。

一个数据的大小和硬盘指针的大小取决于key-value中key和value大小的比值。假如说这个比值是2:1。那么btree浪费了几乎1/3的空间。

       b+tree针对这个问题的,把叶子节点和内节点的数据结构分开设计,让叶子节点不存放指针。因此同样大小的叶子节点,b+tree所能包含数据数量要比btree大。按照上面的假设就是大1/2。数的深度很可能比btree矮,大范围搜索或遍历所需要的载入磁盘的次数也少。


        另外,b+tree还有一个特点是所有数据都存放在叶子节点,这些叶子节点也可以组成一个链表,并把这个链表的表头拿出来,方便直访问数据。有些文章认为这对于范围搜索来说是个巨大的优化。但是就我的看法,这个特性最大的作用仅仅是让代码更容易一些,性能上,只会比树的遍历差,而不会比树的遍历好。因为不管是用指向叶子节点的指针搜,还是用树的遍历搜,所搜索的节点的数量都是几乎相同的。在相同大小的范围搜索的性能,只取决于访问顺序的连续性。从树根向下遍历,那么一次可以取得大量的子节点的范围,并针对这些节点做访问排序,得到更好的访问连续性。如果是沿着指向兄弟节点的指针搜索,一是兄弟节点也许是后插入的,存放并不一定和自己是连续的,二是只有每次从硬盘中将该节点载入到内存,才知道兄弟节点放在硬盘哪个位置,这又变成了对硬盘的一个随机的同步操作,性能的下降可想而知。

      说b+tree因为有指向兄弟节点的指针方便数据库扫库这种结论,是不正确的。


还是上代码吧,依旧只是在内存对数据结构插入删除查找的模拟


be

#!/usr/bin/env Python
from random import randint,choice
from bisect import bisect_right,bisect_left
from collections import deque
class InitError(Exception):
    pass
class ParaError(Exception):
    pass
class KeyValue(object):
    __slots__=('key','value')
    def __init__(self,key,value):
        self.key=key
        self.value=value
    def __str__(self):
        return str((self.key,self.value))
    def __cmp__(self,key):
        if self.key>key:
            return 1
        elif self.key==key:
            return 0
        else:
            return -1
class Bptree_Internode(object):
    def __init__(self,M):
        if not isinstance(M,int):
            raise InitError,'M must be int'
        if M<=3:
            raise InitError,'M must be greater then 3'
        else:
            self.__M=M
            self.clist=[]
            self.ilist=[]
            self.par=None
    def isleaf(self):
        return False
    def isfull(self):
        return len(self.ilist)>=self.M-1
    def isempty(self):
        return len(self.ilist)<=(self.M+1)/2-1
    @property
    def M(self):
        return self.__M
class Bptree_Leaf(object):
    def __init__(self,L):
        if not isinstance(L,int):
            raise InitError,'L must be int'
        else:
            self.__L=L
            self.vlist=[]
            self.bro=None
            self.par=None
    def isleaf(self):
        return True
    def isfull(self):
        return len(self.vlist)>self.L
    def isempty(self):
        return len(self.vlist)<=(self.L+1)/2
    @property
    def L(self):
        return self.__L
class Bptree(object):
    def __init__(self,M,L):
        if L>M:
            raise InitError,'L must be less or equal then M'
        else:
            self.__M=M
            self.__L=L
            self.__root=Bptree_Leaf(L)
            self.__leaf=self.__root
    @property
    def M(self):
        return self.__M
    @property
    def L(self):
        return self.__L
    def insert(self,key_value):
        node=self.__root
        def split_node(n1):
            mid=self.M/2
            newnode=Bptree_InterNode(self.M)
            newnode.ilist=n1.ilist[mid:]
            newnode.clist=n1.clist[mid:]
            newnode.par=n1.par
            for c in newnode.clist:
                c.par=newnode
            if n1.par is None:
                newroot=Bptree_InterNode(self.M)
                newroot.ilist=[n1.ilist[mid-1]]
                newroot.clist=[n1,newnode]
                n1.par=newnode.par=newroot
                self.__root=newroot
            else:
                i=n1.par.clist.index(n1)
                n1.par.ilist.insert(i,n1.ilist[mid-1])
                n1.par.clist.insert(i+1,newnode)
            n1.ilist=n1.ilist[:mid-1]
            n1.clist=n1.clist[:mid]
            return n1.par
        def split_leaf(n2):
            mid=(self.L+1)/2
            newleaf=Bptree_Leaf(self.L)
            newleaf.vlist=n2.vlist[mid:]
            if n2.par==None:
                newroot=Bptree_InterNode(self.M)
                newroot.ilist=[n2.vlist[mid].key]
                newroot.clist=[n2,newleaf]
                n2.par=newleaf.par=newroot
                self.__root=newroot
            else:
                i=n2.par.clist.index(n2)
                n2.par.ilist.insert(i,n2.vlist[mid].key)
                n2.par.clist.insert(i+1,newleaf)
                newleaf.par=n2.par
            n2.vlist=n2.vlist[:mid]
            n2.bro=newleaf
        def insert_node(n):
            if not n.isleaf():
                if n.isfull():
                    insert_node(split_node(n))
                else:
                    p=bisect_right(n.ilist,key_value)
                    insert_node(n.clist[p])
            else:
                p=bisect_right(n.vlist,key_value)
                n.vlist.insert(p,key_value)
                if n.isfull():
                    split_leaf(n)
                else:
                    return
        insert_node(node)
    def search(self,mi=None,ma=None):
        result=[]
        node=self.__root
        leaf=self.__leaf
        if mi is None and ma is None:
            raise ParaError,'you need to setup searching range'
        elif mi is not None and ma is not None and mi>ma:
            raise ParaError,'upper bound must be greater or equal than lower bound'
        def search_key(n,k):
            if n.isleaf():
                p=bisect_left(n.vlist,k)
                return (p,n)
            else:
                p=bisect_right(n.ilist,k)
                return search_key(n.clist[p],k)
        if mi is None:
            while True:
                for kv in leaf.vlist:
                    if kv<=ma:
                        result.append(kv)
                    else:
                        return result
                if leaf.bro==None:
                    return result
                else:
                    leaf=leaf.bro
        elif ma is None:
            index,leaf=search_key(node,mi)
            result.extend(leaf.vlist[index:])
            while True:
                if leaf.bro==None:
                    return result
                else:
                    leaf=leaf.bro
                    result.extend(leaf.vlist)
        else:
            if mi==ma:
                i,l=search_key(node,mi)
                try:
                    if l.vlist[i]==mi:
                        result.append(l.vlist[i])
                        return result
                    else:
                        return result
                except IndexError:
                    return result
            else:
                i1,l1=search_key(node,mi)
                i2,l2=search_key(node,ma)
                if l1 is l2:
                    if i1==i2:
                        return result
                    else:
                        result.extend(l.vlist[i1:i2])
                        return result
                else:
                    result.extend(l1.vlist[i1:])
                    l=l1
                    while True:
                        if l.bro==l2:
                            result.extend(l2.vlist[:i2+1])
                            return result
                        else:
                            result.extend(l.bro.vlist)
                            l=l.bro
    def traversal(self):
        result=[]
        l=self.__leaf
        while True:
            result.extend(l.vlist)
            if l.bro==None:
                return result
            else:
                l=l.bro
    def show(self):
        print 'this b+tree is:\n'
        q=deque()
        h=0
        q.append([self.__root,h])
        while True:
            try:
                w,hei=q.popleft()
            except IndexError:
                return
            else:
                if not w.isleaf():
                    print w.ilist,'the height is',hei
                    if hei==h:
                        h+=1
                    q.extend([[i,h] for i in w.clist])
                else:
                    print [v.key for v in w.vlist],'the leaf is,',hei
                                                                                                                                                                                                                                                                                                                                  
    def delete(self,key_value):
        def merge(n,i):
            if n.clist[i].isleaf():
                n.clist[i].vlist=n.clist[i].vlist+n.clist[i+1].vlist
                n.clist[i].bro=n.clist[i+1].bro
            else:
                n.clist[i].ilist=n.clist[i].ilist+[n.ilist[i]]+n.clist[i+1].ilist
                n.clist[i].clist=n.clist[i].clist+n.clist[i+1].clist
            n.clist.remove(n.clist[i+1])
            n.ilist.remove(n.ilist[i])
            if n.ilist==[]:
                n.clist[0].par=None
                self.__root=n.clist[0]
                del n
                return self.__root
            else:
                return n
        def tran_l2r(n,i):
            if not n.clist[i].isleaf():
                n.clist[i+1].clist.insert(0,n.clist[i].clist[-1])
                n.clist[i].clist[-1].par=n.clist[i+1]
                n.clist[i+1].ilist.insert(0,n.ilist[i])
                n.ilist[i]=n.clist[i].ilist[-1]
                n.clist[i].clist.pop()
                n.clist[i].ilist.pop()
            else:
                n.clist[i+1].vlist.insert(0,n.clist[i].vlist[-1])
                n.clist[i].vlist.pop()
                n.ilist[i]=n.clist[i+1].vlist[0].key
        def tran_r2l(n,i):
            if not n.clist[i].isleaf():
                n.clist[i].clist.append(n.clist[i+1].clist[0])
                n.clist[i+1].clist[0].par=n.clist[i]
                n.clist[i].ilist.append(n.ilist[i])
                n.ilist[i]=n.clist[i+1].ilist[0]
                n.clist[i+1].clist.remove(n.clist[i+1].clist[0])
                n.clist[i+1].ilist.remove(n.clist[i+1].ilist[0])
            else:
                n.clist[i].vlist.append(n.clist[i+1].vlist[0])
                n.clist[i+1].vlist.remove(n.clist[i+1].vlist[0])
                n.ilist[i]=n.clist[i+1].vlist[0].key
        def del_node(n,kv):
            if not n.isleaf():
                p=bisect_right(n.ilist,kv)
                if p==len(n.ilist):
                    if not n.clist[p].isempty():
                        return del_node(n.clist[p],kv)
                    elif not n.clist[p-1].isempty():
                        tran_l2r(n,p-1)
                        return del_node(n.clist[p],kv)
                    else:
                        return del_node(merge(n,p),kv)
                else:
                    if not n.clist[p].isempty():
                        return del_node(n.clist[p],kv)
                    elif not n.clist[p+1].isempty():
                        tran_r2l(n,p)
                        return del_node(n.clist[p],kv)
                    else:
                        return del_node(merge(n,p),kv)
            else:
                p=bisect_left(n.vlist,kv)
                try:
                    pp=n.vlist[p]
                except IndexError:
                    return -1
                else:
                    if pp!=kv:
                        return -1
                    else:
                        n.vlist.remove(kv)
                        return 0
        del_node(self.__root,key_value)
def test():
    mini=2
    maxi=60
    testlist=[]
    for i in range(1,10):
        key=i
        value=i
        testlist.append(KeyValue(key,value))
    mybptree=Bptree(4,4)
    for kv in testlist:
        mybptree.insert(kv)
    mybptree.delete(testlist[0])
    mybptree.show()
    print '\nkey of this b+tree is \n'
    print [kv.key for kv in mybptree.traversal()]
    #print [kv.key for kv in mybptree.search(mini,maxi)]
if __name__=='__main__':
    test()

实现过程和btree很像,不过有几点显著不同。

1.内节点不存储key-value,只存放key

2.沿着内节点搜索的时候,查到索引相等的数要向树的右边走。所以二分查找要选择bisect_right

3.在叶子节点满的时候,并不是先分裂再插入而是先插入再分裂。因为b+tree无法保证分裂的两个节点的大小都是相等的。在奇数大小的数据分裂的时候右边的子节点会比左边的大。如果先分裂再插入无法保证插入的节点一定会插在数量更少的子节点上,满足节点数量平衡的条件。

4.在删除数据的时候,b+tree的左右子节点借数据的方式比btree更加简单有效,只把子节点的子树直接剪切过来,再把索引变一下就行了,而且叶子节点的兄弟指针也不用动。


--结束END--

本文标题: 关于B+tree (附python 模

本文链接: https://www.lsjlt.com/news/191139.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 关于B+tree (附python 模
    前几天我写了点btree的东西(http://thuhak.blog.51cto.com/2891595/1261783),今天继续这个思路,继续写b+tree。而且b+tree才是我的目的,更加深入理解文件和数据库索引的基本原理。在之前,...
    99+
    2023-01-31
    tree python
  • 关于vue-tree-chart简单的使用
    目录vue-tree-chart的使用插件安装组件内容:vart.vue页面中使用vue-tree-chartview视图 about组件封装的treeChart组件vue-tree...
    99+
    2022-11-13
    vue-tree-chart Vue tree vue chart
  • 关于python netsnmp模块 s
    写一个测试脚本:costtime.py #!/usr/bin/python #encoding=utf-8 #description:测试netsnmp.snmpwalk中Timeout值对应的具体时间 #filename:cost...
    99+
    2023-01-31
    模块 python netsnmp
  • 【9】python关于os模块与os.p
      ---恢复内容开始---     #__author:"吉*佳" #date: 2018/10/20 0020 #function: # os模块知识点 import os # 获取平台名称: 打印:nt代表windows ...
    99+
    2023-01-30
    模块 python os
  • 关于Python常用模块时间模块time
    目录time简介导入模块1.时间戳2.时间元组(1)把时间戳转换为元组形式(2)元组转换为时间戳输出(3)把元组转换为格式化时间(4)把时间戳转换为格式化时间3.字符串时间(重点)(...
    99+
    2023-05-16
    Python模块 Python时间模块 Python time模块
  • 关于python中pika模块的问题
    工作中经常用到rabbitmq,而用的语言主要是python,所以也就经常会用到python中的pika模块,但是这个模块的使用,也给我带了很多问题,这里整理一下关于这个模块我在使用过程的改变历程已经中间碰到一些问题 的解决方法 刚开写代...
    99+
    2023-01-30
    模块 python pika
  • 关于python 缺少dbm模块问题
    今天在 CentOS 5.6  64位的机器上配置Func被控端时,在安装设置完 Func 及 Certmaster 后,启动 Funcd 提示如下错误 [root@certmaster ~]#service funcd start  St...
    99+
    2023-01-31
    模块 python dbm
  • 关于Python正则表达式模块之re模块
    目录前言:导入模块1.re.match() 函数(1)匹配单个字符(2)匹配多个字符 字符功能/说明位置*(3) 匹配开头和结尾2.re.search() 函数3.re.findal...
    99+
    2023-05-16
    Python正则表达式 Pythonre模块
  • 关于Java的二叉树、红黑树、B+树详解
    目录1、二叉查找树2、平衡二叉查找树3、红黑树:4、 B树:5、 B+树6、红黑树 VS B+树数组和链表是常用的数据结构,数组虽然查找快(有序数组可以通过二分法查找),但是插入和删...
    99+
    2023-05-20
    Java二叉树 Java红黑树 JavaB+树
  • 关于MySQL B+树索引与哈希索引详解
    目录索引介绍B+树索引优点缺点哈希索引优点缺点补充:二者区别总结 索引介绍 索引是一种特殊的数据库结构,被设计用来快速查询数据库表中的特定记录。索引有多种类型,就像字典有拼...
    99+
    2024-04-02
  • 关于python中模块和重载的问题
    目录模块和重载模块与命名空间模块和重载 简单来讲,任意一个以.py结尾的python文件都是一个模块。例如有A.py和B.py两个文件。在A中可以通过导入B来读取B模块定义的内容,导...
    99+
    2024-04-02
  • 关于python multiproces
    这两天温故了python 的multiprocessing多进程模块,看到的pipe和queue这两种ipc方式,啥事ipc? ipc就是进程间的通信模式,常用的一半是socke,rpc,pipe和消息队列等。 今个就再把pipe和queu...
    99+
    2023-01-31
    python multiproces
  • python中argparse模块关于 parse_args() 函数详解(全)
    目录 前言1. 函数讲解2. 基本用法3. 实战讲解 前言 原理:命令行解析使用argparse包作用:命令行传参赋值 可用在机器学习深度学习 或者 脚本运行等 了解这个函数需要了解其背后...
    99+
    2023-09-23
    python 人工智能
  • 关于Python中zipfile压缩包模块的使用
    目录简介解压文件是否ZIP文件读取元数据从其他数据源压缩文件写入ZipInfo追加文件创建包含Python库的ZIP简介 ZIP 文件格式是一个常用的归档与压缩标准,zipfile&...
    99+
    2023-05-15
    Python zipfile Python 压缩包模块 zipfile压缩包模块
  • 关于python和sudo python
    之前在搞ssd的时候没出问题,后来重装啦一下系统,把它拷回来,发现出了点问题,在训练或者测试的时候,需要输入:python examples/ssd/ssd_pascal.py 或者python examples/ssd/score_ss...
    99+
    2023-01-31
    python sudo
  • 关于Python的JSON
    1、json模块load/loads、dump/dumps区别:(摘自这里)实际上json就是python字典的字符串表示,但是字典作为一个复杂对象是无法直接转换成定义它的代码的字符串,python有一个叫 simplejson的库可以方便...
    99+
    2023-01-31
    Python JSON
  • 关于Python中模块的简介、定义与使用
    目录前言:1.什么是模块2.模块的分类3.模块的使用4.自定义模块5.模块和执行文件的判断前言: 今天就开始讲Python中的模块篇了,模块是Python的重要组成部分,Python...
    99+
    2023-05-16
    Python模块定义 Python模块使用
  • 基于Python制作B站视频下载小工具
    目录1. 原理简介2. 网页分析3. 视频爬取4. 存入本地5. GUI工具制作1. 原理简介 原理很简单,就是获取视频资源的源地址,然后爬取视频的二进制内容,再写入到本地即可。 2...
    99+
    2024-04-02
  • Python - 关于Python的变量
    Python的变量是动态的,不需要预先申明,当赋值时自动创建变量,并且Python变量存储的是对象的引用(非变量本身)。Python变量的命名规则与C语言相似,并且在日常使用中一般会遵循以下一些规则:A. 一般不以单下划线“_”开头,因为以...
    99+
    2023-01-31
    变量 Python
  • 【python】用SMTP模块发送带附件
    第一篇博客!参考链接⬅ 在书上看了用SMTP模块发邮件,试过之后发现并没有什么用。163邮箱开启了SMTP服务后,登陆了发送的时候却被拒收了。 找了前人的资料,发现被过期的教程害死了。 以下代码有效: import smtplib fr...
    99+
    2023-01-30
    模块 附件 python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作