/*
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
}
js版本b-tree的删除(写得太烂,无法参考)
最新推荐文章于 2023-04-17 22:14:56 发布