Skip to content

Latest commit

 

History

History
122 lines (117 loc) · 4.33 KB

4E.md

File metadata and controls

122 lines (117 loc) · 4.33 KB

基数树

基数树就是一种简单有效的分级索引结构,每层索引为键值的一个片段。

type node struct {
    kids [Radix]unsafe.Pointer
}

func branch(key uint, depth uint) uint {
    return (key >> (bitWidth - (depth+1)*Step)) & Mask
}

基数树的操作比较简单,删除时注意级联处理就好。

func (m *Map) Remove(key uint) bool {
    var path [Depth]*node
    path[0] = &m.root
    for i := uint(0); i < Depth-1; i++ {
        path[i+1] = (*node)(path[i].kids[branch(key, i)])
        if path[i+1] == nil { return false }
    }
    idx := key & Mask
    if path[Depth-1].kids[idx] == nil { return false }
    path[Depth-1].kids[idx] = nil
    for i := Depth - 1; i != 0; i-- {
        j := uint(0)
        for j < Radix && path[i].kids[j] == nil { j++ }
        if j == Radix { //全空
            path[i-1].kids[branch(key, i-1)] = nil
    }   }
    return true
}

字典树

  字典树又称前缀树,是一种针对序列的索引结构。字典树的基本原理和基数树一样,不过由于序列长度并不固定,情况要更加复杂一些。另一方面,我们通常希望数据单元是有意义的片段(不宜小于一字节),导致节点的后继可能较多,故如何获得较好的空间利用率也是一项挑战。字典树节点的结构有多种,各有千秋,而这里介绍的一种分离指针数组结构则兼顾了便捷与效能。

type node struct {
    data [segCap]byte     //保存若干数据单元
    mark uint8            //高位一bit表示是否实体,低位表示段长
    kids []*node
}

字典树的插入相对容易,注意处理分裂即可。

func (t *Trie) insert(key string) bool {
    unit, skip := &t.root, uint8(0)
    unit.compact()                              //在查询之前尝试节缩
    brk := 0
    for sz := unit.mark & 0x7f; len(key) > int(sz); sz = unit.mark & 0x7f {
        brk = unit.diff(key, skip, sz)
        if brk >= 0 { goto Lsplit }
        skip = 1
        key = key[sz:]
        pos := search(unit.kids, key[0])
        if pos >= len(unit.kids) || unit.kids[pos].data[0] != key[0] {
            unit.kids = array.InsertTo(unit.kids, pos, createTail(key))
            return true                         //在关节点增加分支
        }
        unit = unit.kids[pos]
        unit.compact()                          //在查询之前尝试节缩
    }
    brk = unit.diff(key, skip, uint8(len(key)))
    if brk < 0 {
        if len(key) == int(unit.mark&0x7f) {    //停在关节点,直接打标
            done := (unit.mark & 0x80) == 0
            unit.mark |= 0x80
            return done
        } else {                                //单分裂
            unit.kids = []*node{unit.split(len(key))}
            unit.mark |= 0x80
            return true
    }   }
Lsplit:                                         //双分裂
    kid1 := createTail(key[brk:])
    kid2 := unit.split(brk)
    if kid1.data[0] > kid2.data[0] {
        kid1, kid2 = kid2, kid1
    }
    unit.kids = []*node{kid1, kid2}
    return true
}

而删除则要考虑删除非共享序列片段。

func (t *Trie) remove(key string) bool {
    knot, branch := (*node)(nil), -1            //追踪删除关节
    unit, skip := &t.root, uint8(0)
    unit.compact()
    for sz := unit.mark & 0x7f; len(key) > int(sz); sz = unit.mark & 0x7f {
        if (unit.mark&0x80) != 0 || len(unit.kids) > 1 {
            knot = unit
        }
        if unit.diff(key, skip, sz) >= 0 { return false }
        skip = 1
        key = key[sz:]
        pos := search(unit.kids, key[0])
        if pos >= len(unit.kids) || unit.kids[pos].data[0] != key[0] {
            return false
        }
        if knot == unit { branch = pos }
        unit = unit.kids[pos]
        unit.compact()
    }
    if (len(key)|0x80) == int(unit.mark) &&
        unit.diff(key, skip, unit.mark&0x7f) < 0 {
        if len(unit.kids) == 0 && knot != nil { //删除非共享序列片段
            knot.kids = array.EraseFrom(knot.kids, branch, true)
        } else {
            unit.mark &= 0x7f
        }
        return true
    }
    return false
}

目录 上一节 下一节