js版本b-tree的删除(写得太烂,无法参考)

/*
get和put方法见https://blog.csdn.net/weixin_43292547/article/details/104071214,
为了测试删除,原本用来测试get和put的代码改了很多。
删除用了3个方法,del和delete_和balance。下面有详细注释。
使用
"节点"的数据结构:list:[node1,node2,....]
真正的节点的数据结构:node1:{k:1,v:'v1',list:list1}
结构的删除方法的逻辑非常麻烦,暂时没找到更好的逻辑/写法。
*/
let M=100
let N=1000*1000

var root=[{k:-1}]
var initList=[]
for(i=0;i<N;i++){
    initList.push(i)
}

for(i=0;i<N;i++){
    root=put(root,initList[i],'v'+initList[i],M)
}

testGet(root,N)

testDelete(root,N+3)
testDelete(root,4.3)
for(let i=0;i<Math.min(5,N/10);i++){
    let deleteK=Math.floor(Math.random()*M)
    testDelete(root,deleteK)
    if(i%3==0){
        testDelete(root,deleteK)
    }
}




function put(list,k,v,M=M){
    list=put_(list,k,v,M=M)
    if(list.length==M){
        list=split(list,M=M)
    }
    return list
}


function put_(list,k,v,M=M){
    var index=binarySearch(list,k)
    var node=list[index]
    var node1=list[index-1]
    if(node==undefined&&node1.list==undefined){
        list[index]={k:k,v:v}
    }else{
        if((node!=undefined&&node.k!=k)||(node==undefined)){
            var node=list[--index]
        }
        if(node.list!=undefined){
            tmpl=put_(list[index].list,k,v,M)
            if(tmpl.length==M){
                tmplist=split(tmpl,M=M)
                list[index]=tmplist[0]
                list.splice(index+1,0,tmplist[1])
            }else{
                list[index].list=tmpl
            }
        }else{
            if(node.k==k){
                list[index].v=v
            }else{
                list.splice(index+1,0,{k:k,v:v})
            }
        }
    }
    return list
}



function split(list,M=M){
    let m=M/2
    let list2=list.splice(m,m)
    let node1={k:list[0].k,v:list[0].v,list:list}
    let node2={k:list2[0].k,v:list2[0].v,list:list2}
    return [node1,node2]
}

function get(list,k){
    if(list==undefined)return
    let index=binarySearch(list,k,0,list.length)
    let node=list[index]
    if(node==undefined){
        node=list[index-1]
        if(node.list==undefined){
            return
        }
    }
    if(node.k==k){
        if(node.list==undefined){
            return node
        }else{
            return get(node.list,k)
        }
    }else{
        return get(list[index-1].list,k)
    }
}

function binarySearch(list,k,left=0,right=list.length){
    let mid=Math.floor((right-left)/2)+left
    let node=list[mid]
    if(left>right||node==undefined){
        return left
    }
    if(node.k==k){                            //一开始写成if(node.k=k){,找了好久bug
        return mid
    }else if(node.k<k){
        return binarySearch(list,k,mid+1,right)
    }else{
        return binarySearch(list,k,left,mid-1)
    }
}

function randomSort(){
    return Math.random()-.5
}


function print(list,M){
    var queue=JSON.parse(JSON.stringify(list))
    var count=-1
    while(queue.length>0){
        let node=queue.shift()
        node.depth=(node.depth==undefined?0:node.depth)
        console.log(node)
        var tmpl=node.list
        if(tmpl==undefined){
            if(count++!=node.k){
                //throw new Error("print countError")
            }
            continue
        }
        if(tmpl.length<M/2||tmpl.length>M){
            //throw new Error("length Error")
        }
        for(let i=0;i<tmpl.length;i++){
            var tmpnode=tmpl[i]
            tmpnode.parent=node.k
            tmpnode.depth=node.depth+1
            queue.push(tmpnode)
        }
    }
    console.log('b-tree深度-->',tmpnode.depth)
}

function testGet(root,N=N){
    console.log('testGet begin----->')
    for(i=0;i<N;i++){
        if(get(root,i)==undefined||get(root,i).k!=i){
            //throw new Error('testGet failed')
            console.log('\tcan not find ',i)
        }
        if(get(root,i-0.5)!=undefined){
            throw new Error('testGet failed')
        }
    }
    if(get(root,N+2)!=undefined){
        throw new Error('testGet failed')
    }
    //console.log('testGet success')
    console.log('<-----testGet end')
}


function testDelete(root,k){
    console.log('\ntestDelete-------------->',k)
    root=del(root,k,M=M)
    //print(root,M)
    testGet(root,N)
    console.log('<--------------testDelete\n',k)
}



/*
balance:删除list的node的list1的node1后,list1的长度可能少于M/2,balance用来修正这个。
删除的逻辑如下:
1,找到需要删除的node,它一定是叶子节点,可能存在,也可能不存在,删除此node;
2,父list判断删除此node的list的长度(不在此node所在list判断是因为修正必须在父list进行),长度小于M/2时需要balance;
    balace操作:
        1,找到兄弟list(与指向此node所在list的node的相邻node指向的list),优先找右,没有右再找左(哪个优先应该没关系)
        2,修正
            2.1,如果兄弟list长度等于M/2,则合并,父list删除右边list的node(兄弟在左删自己,兄弟在右删兄弟);
            2.2,如果兄弟list长度大于M/2,借来兄弟指针节点,兄弟list换指针节点(兄弟在左借最右边,兄弟在右借最左边);
            需要注意两点:
                1,合并或者借完节点后,一定要修正父list的指针,自己和兄弟的可能都要修正;
                2,被删除的节点可能是连续的最左节点(比如k=-1这样的node),但兄弟借来的节点一定不会
是连续的最左节点(因为兄弟在右边时才可能借左节点,但如果这个左节点是连续的最左节点,那它就不可能属于兄弟),所以不需要考虑这种情况。
3,修复连续的最左节点。比如[{k:1},{k:3}]里1的list是[{k:1},{k:2}],现在删除[{k:1},{k:2}]的1变成[{k:2}],此时[{k:1},{k:2}]的1依旧指向[{k:2}],出现bug,这里只需改变1的k值,把[{k:1},{k:3}]改成[{k:2},{k:3}]即可(不要修改1的list属性)。balance修复后的list会修复k值不会出这个问题,但如果没有进入balance会出问题,所以需要在delete_里修正。要注意经过delete_递归后父list可能变短导致被修改的子list的索引不再是index,而是index-1,所以需要对index和index-1都进行了k值的修正。
*/

function del(list,k,M=M){
    list=delete_(list,k,M=M)
    return list
}


function delete_(list,k,M=M){
    var index=binarySearch(list,k)
    var node=list[index]
    var node1=list[index-1]
    if(node==undefined&&node1.list==undefined){
        return list
    }else{
        if((node!=undefined&&node.k!=k)||(node==undefined)){
            var node=list[--index]
        }
        if(node.list!=undefined){
            tmpl=delete_(list[index].list,k,M)
            if(tmpl.length<M/2){
                list=balance(list,index,tmpl)
            }
            var knode=list[index]
            if(list[index]!=undefined){
                list[index].k=knode.list[0].k
            }
            var knode=list[--index]
            if(list[index]!=undefined){
                list[index].k=knode.list[0].k
            }
        }else{
            if(node.k==k){
                list.splice(index,1)
            }
        }
    }
    return list
}

//删除list的node的list1的node1后,list1的长度可能少于M/2,所以需要修正。balance用来修正这个
function balance(list,index,tmpl){
    var bro=list[index+1]
    if(bro==undefined){
        bro=list[index-1]
        var brol=bro.list
        if(brol.length==M/2){
            list[index-1].list=brol.concat(tmpl)
            list.splice(index,1)
        }else{
            let bnode=brol.pop()
            tmpl.unshift(bnode)
            list[index].k=bnode.k
            list[index].v=bnode.v
            list[index].list=tmpl
        }
    }else{
        var brol=bro.list
        if(brol.length==M/2){
            list[index].list=tmpl.concat(brol)
            list.splice(index+1,1)
        }else{
            let bnode=brol.shift()
            tmpl.push(bnode)
            list[index+1].k=brol[0].k
            list[index+1].v=brol[0].v
            list[index].list=tmpl
        }
    }
    return list
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值