From 2aec515340660c4bd260be36d2185ed5190978d6 Mon Sep 17 00:00:00 2001 From: hlccd <56643462+hlccd@users.noreply.github.com> Date: Sun, 19 Dec 2021 23:39:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E5=89=8D=E7=BC=80?= =?UTF-8?q?=E5=9F=BA=E6=95=B0=E6=A0=91Rax=E7=9A=84=E5=AE=9E=E7=8E=B0,?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E4=BF=AE=E7=BC=AE=E4=BA=86=E4=B9=8B=E5=89=8D?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=9A=84=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=B8=AD=E5=87=BA=E7=8E=B0=E7=9A=84=E4=B8=80=E4=BA=9B=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- goSTL/data_structure/rax/node.go | 221 ++++++++++++++++++++++ goSTL/data_structure/rax/rax.go | 303 ++++++++++++++++++++++++++++++ goSTL/data_structure/trie/node.go | 13 +- goSTL/data_structure/trie/trie.go | 5 +- 4 files changed, 539 insertions(+), 3 deletions(-) create mode 100644 goSTL/data_structure/rax/node.go create mode 100644 goSTL/data_structure/rax/rax.go diff --git a/goSTL/data_structure/rax/node.go b/goSTL/data_structure/rax/node.go new file mode 100644 index 0000000..3c8047d --- /dev/null +++ b/goSTL/data_structure/rax/node.go @@ -0,0 +1,221 @@ +package rax + +//@Title rax +//@Description +// 前缀基数树的节点 +// 可通过节点的分叉对string进行查找 +// 增添string时候需要增删结点,同时将结点内置的map中增删删除对应的string即可 +// 当string到终点时存储元素 + +//node树节点结构体 +//该节点是rax的树节点 +//结点存储到此时的string的前缀数量 +//son存储其下属分叉的子结点指针 +//该节点同时存储其元素 +type node struct { + name string //以当前结点的string内容 + num int //以当前结点为前缀的数量 + value interface{} //当前结点存储的元素 + sons map[string]*node //该结点下属结点的指针 +} + +//@title newNode +//@description +// 新建一个前缀基数树节点并返回 +// 将传入的元素e作为该节点的承载元素 +//@receiver nil +//@param name string 该节点的名字,即其对应的string +//@param e interface{} 承载元素e +//@return n *node 新建的单词查找树节点的指针 +func newNode(name string, e interface{}) (n *node) { + return &node{ + name: name, + num: 0, + value: e, + sons: make(map[string]*node), + } +} + +//@title inOrder +//@description +// 以node前缀基数树节点做接收者 +// 遍历其分叉以找到其存储的所有string +//@receiver n *node 接受者node的指针 +//@param s string 到该结点时的前缀string +//@return es []interface{} 以该前缀s为前缀的所有string的集合 +func (n *node) inOrder(s string) (es []interface{}) { + if n == nil { + return es + } + if n.value != nil { + es = append(es, s+n.name) + } + for _, son := range n.sons { + es = append(es, son.inOrder(s+n.name+"/")...) + } + return es +} + +//@title insert +//@description +// 以node前缀基数树节点做接收者 +// 从n节点中继续插入以s为索引的元素e,且当前抵达的string位置为p +// 若该层string为""时候视为失败 +// 当到达s终点时进行插入,如果此时node承载了元素则插入失败,否则成功 +// 当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可 +// 当插入失败且对应子结点为新建节点时则需要删除该子结点 +//@receiver n *node 接受者node的指针 +//@param ss []string 待删除元素的索引s的按'/'进行分层的索引集合 +//@param p int 索引当前抵达的位置 +//@param e interface{} 待插入元素e +//@return b bool 是否插入成功? +func (n *node) insert(ss []string, p int, e interface{}) (b bool) { + if p == len(ss) { + if n.value != nil { + return false + } + n.value = e + n.num++ + return true + } + s := ss[p] + if s == "" { + return false + } + //从其子结点的map中找到对应的方向 + son, ok := n.sons[s] + if !ok { + //不存在,新建并放入map中 + son = newNode(s, nil) + n.sons[s] = son + } + //从子结点对应方向继续插入 + b = son.insert(ss, p+1, e) + if b { + n.num++ + } else { + if !ok { + //插入失败且该子节点为新建结点则需要删除该子结点 + delete(n.sons, s) + } + } + return b +} + +//@title erase +//@description +// 以node前缀基数树节点做接收者 +// 从n节点中继续删除以s为索引的元素e,且当前抵达的string位置为p +// 若该层string为""时候视为失败 +// 当到达s终点时进行删除,如果此时node未承载元素则删除失败,否则成功 +// 当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可,若其分叉为nil则直接失败 +//@receiver n *node 接受者node的指针 +//@param ss []string 待删除元素的索引s的按'/'进行分层的索引集合 +//@param p int 索引当前抵达的位置 +//@return b bool 是否删除成功? +func (n *node) erase(ss []string, p int) (b bool) { + if p == len(ss) { + if n.value != nil { + n.value = nil + n.num-- + return true + } + return false + } + //从map中找到对应下子结点位置并递归进行删除 + s := ss[p] + son, ok := n.sons[s] + if !ok || son == nil { + //未找到或son不存在,删除失败 + return false + } + b = son.erase(ss, p+1) + if b { + n.num-- + if son.num <= 0 { + //删除后子结点的num<=0即该节点无后续存储元素,可以销毁 + delete(n.sons, s) + } + } + return b +} + +//@title delete +//@description +// 以node前缀基数树节点做接收者 +// 从n节点中继续删除以s为索引的元素e,且当前抵达的string位置为p +// 若该层string为""时候视为失败 +// 当到达s终点时进行删除,删除所有后续元素,并返回其后续元素的数量 +// 当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可,若其分叉为nil则直接返回0 +//@receiver n *node 接受者node的指针 +//@param ss []string 待删除元素的索引s的按'/'进行分层的索引集合 +//@param p int 索引当前抵达的位置 +//@return num int 被删除元素的数量 +func (n *node) delete(ss []string, p int) (num int) { + if p == len(ss) { + return n.num + } + //从map中找到对应下子结点位置并递归进行删除 + s := ss[p] + son, ok := n.sons[s] + if !ok || son == nil { + return 0 + } + num = son.delete(ss, p+1) + if num > 0 { + n.num -= num + if son.num <= 0 { + //删除后子结点的num<=0即该节点无后续存储元素,可以销毁 + delete(n.sons, s) + } + } + return num +} + +//@title count +//@description +// 以node前缀基数树节点做接收者 +// 从n节点中继续查找以s为前缀索引的元素e,且当前抵达的string位置为p +// 若该层string为""时候视为查找失败 +// 当到达s终点时返回其值即可 +// 当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可,当其分叉为nil则直接返回0 +//@receiver n *node 接受者node的指针 +//@param ss []string 待删除元素的索引s的按'/'进行分层的索引集合 +//@param p int 索引当前抵达的位置 +//@return num int 以该s为前缀的string的数量 +func (n *node) count(ss []string, p int) (num int) { + if p == len(ss) { + return n.num + } + //从map中找到对应下子结点位置并递归进行查找 + s := ss[p] + son, ok := n.sons[s] + if !ok || son == nil { + return 0 + } + return son.count(ss, p+1) +} + +//@title find +//@description +// 以node前缀基数树节点做接收者 +// 从n节点中继续查找以s为前缀索引的元素e,且当前抵达的string位置为p +// 若该层string为""时候视为查找失败 +// 当到达s终点时返回其承载的元素即可 +// 当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可,当其分叉为nil则直接返回nil +//@receiver n *node 接受者node的指针 +//@param ss []string 待删除元素的索引s的按'/'进行分层的索引集合 +//@param p int 索引当前抵达的位置 +//@return e interface{} 该索引所指向的元素e +func (n *node) find(ss []string, p int) (e interface{}) { + if p == len(ss) { + return n.value + } + //从map中找到对应下子结点位置并递归进行查找 + s := ss[p] + son, ok := n.sons[s] + if !ok || son == nil { + return 0 + } + return son.find(ss, p+1) +} diff --git a/goSTL/data_structure/rax/rax.go b/goSTL/data_structure/rax/rax.go new file mode 100644 index 0000000..5dc8250 --- /dev/null +++ b/goSTL/data_structure/rax/rax.go @@ -0,0 +1,303 @@ +package rax + +//@Title rax +//@Description +// 前缀基数树-Rax +// 以多叉树的形式实现,根据'/'进行string分割,将分割后的string数组进行段存储 +// 插入的string首字符必须为'/' +// 任意一段string不能为"" +// 结点不允许覆盖,即插入值已经存在时会插入失败,需要先删除原值 +// 使用互斥锁实现并发控制 + +import ( + "github.com/hlccd/goSTL/utils/iterator" + "strings" + "sync" +) + +//rax前缀基数树结构体 +//该实例存储前缀基数树的根节点 +//同时保存该树已经存储了多少个元素 +//整个树不允许重复插入,若出现重复插入则直接失败 +type rax struct { + root *node //前缀基数树的根节点指针 + size int //当前已存放的元素数量 + mutex sync.Mutex //并发控制锁 +} + +//rax前缀基数树容器接口 +//存放了rax前缀基数树可使用的函数 +//对应函数介绍见下方 +type raxer interface { + Iterator() (i *Iterator.Iterator) //返回包含该rax的所有string + Size() (num int) //返回该rax中保存的元素个数 + Clear() //清空该rax + Empty() (b bool) //判断该rax是否为空 + Insert(s string, e interface{}) (b bool) //向rax中插入string并携带元素e + Erase(s string) (b bool) //从rax中删除以s为索引的元素e + Delete(s string) (num int) //从rax中删除以s为前缀的所有元素 + Count(s string) (num int) //从rax中寻找以s为前缀的string单词数 + Find(s string) (e interface{}) //从rax中寻找以s为索引的元素e +} + +//@title New +//@description +// 新建一个rax前缀基数树容器并返回 +// 初始根节点为nil +//@receiver nil +//@param nil +//@return r *rax 新建的rax指针 +func New() (r *rax) { + return &rax{ + root: newNode("", nil), + size: 0, + mutex: sync.Mutex{}, + } +} + +//@title Iterator +//@description +// 以rax前缀基数树做接收者 +// 将该rax中所有存放的string放入迭代器中并返回 +//@receiver r *rax 接受者rax的指针 +//@param nil +//@return i *iterator.Iterator 新建的Iterator迭代器指针 +func (r *rax) Iterator() (i *Iterator.Iterator) { + if r == nil { + return nil + } + r.mutex.Lock() + es := r.root.inOrder("") + i = Iterator.New(&es) + r.mutex.Unlock() + return i +} + +//@title Size +//@description +// 以rax前缀基数树做接收者 +// 返回该容器当前含有元素的数量 +// 如果容器为nil返回0 +//@receiver r *rax 接受者rax的指针 +//@param nil +//@return num int 容器中实际使用元素所占空间大小 +func (r *rax) Size() (num int) { + if r == nil { + return 0 + } + if r.root == nil { + return 0 + } + return r.size +} + +//@title Clear +//@description +// 以rax前缀基数树做接收者 +// 将该容器中所承载的元素清空 +// 将该容器的size置0 +//@receiver r *rax 接受者rax的指针 +//@param nil +//@return nil +func (r *rax) Clear() { + if r == nil { + return + } + r.mutex.Lock() + r.root = newNode("", nil) + r.size = 0 + r.mutex.Unlock() +} + +//@title Empty +//@description +// 以rax前缀基数树做接收者 +// 判断该rax是否含有元素 +// 如果含有元素则不为空,返回false +// 如果不含有元素则说明为空,返回true +// 如果容器不存在,返回true +//@receiver r *rax 接受者rax的指针 +//@param nil +//@return b bool 该容器是空的吗? +func (r *rax) Empty() (b bool) { + if r == nil { + return true + } + return r.size == 0 +} + +//@title Insert +//@description +// 以rax前缀基数树做接收者 +// 向rax插入以string类型的s为索引的元素e +// 插入的string的首字符必须为'/',且中间按'/'分割的string不能为"" +// 若存在重复的s则插入失败,不允许覆盖 +// 否则插入成功 +//@receiver r *rax 接受者rax的指针 +//@param s string 待插入元素的索引s +//@param e interface{} 待插入元素e +//@return b bool 添加成功? +func (r *rax) Insert(s string, e interface{}) (b bool) { + if r == nil { + return false + } + if len(s) == 0 { + return false + } + if s[0] != '/' { + return false + } + //将s按'/'进行分割,并去掉第一个即去掉"",随后一次按照分层结果进行插入 + ss := strings.Split(s, "/")[1:] + r.mutex.Lock() + if r.root == nil { + //避免根节点为nil + r.root = newNode("", nil) + } + //从根节点开始插入 + b = r.root.insert(ss, 0, e) + if b { + //插入成功,size+1 + r.size++ + } + r.mutex.Unlock() + return b +} + +//@title Erase +//@description +// 以rax前缀基数树做接收者 +// 从rax树中删除元素以s为索引的元素e +// 用以删除的string索引的首字符必须为'/' +//@receiver r *rax 接受者rax的指针 +//@param s string 待删除元素的索引 +//@return b bool 删除成功? +func (r *rax) Erase(s string) (b bool) { + if r.Empty() { + return false + } + if len(s) == 0 { + return false + } + if s[0] != '/' { + return false + } + //将s按'/'进行分割,并去掉第一个即去掉"",随后一次按照分层结果进行删除 + ss := strings.Split(s, "/")[1:] + if r.root == nil { + //根节点为nil即无法删除 + return false + } + r.mutex.Lock() + //从根节点开始删除 + b = r.root.erase(ss, 0) + if b { + //删除成功,size-1 + r.size-- + if r.size == 0 { + //所有string都被删除,根节点置为nil + r.root = nil + } + } + r.mutex.Unlock() + return b +} + +//@title Delete +//@description +// 以rax前缀基数树做接收者 +// 从rax树中删除以s为前缀的所有元素 +// 用以删除的string索引的首字符必须为'/' +//@receiver r *rax 接受者rax的指针 +//@param s string 待删除元素的前缀 +//@return num int 被删除的元素的数量 +func (r *rax) Delete(s string) (num int) { + if r.Empty() { + return 0 + } + if len(s) == 0 { + return 0 + } + if s[0] != '/' { + return 0 + } + if r.root == nil { + return 0 + } + //将s按'/'进行分割,并去掉第一个即去掉"",随后一次按照分层结果进行删除 + ss := strings.Split(s, "/")[1:] + r.mutex.Lock() + //从根节点开始删除 + num = r.root.delete(ss, 0) + if num > 0 { + //删除成功 + r.size -= num + if r.size <= 0 { + //所有string都被删除,根节点置为nil + r.root = nil + } + } + r.mutex.Unlock() + return num +} + +//@title Count +//@description +// 以rax前缀基数树做接收者 +// 从rax中查找以s为前缀的所有string的个数 +// 用以查找的string索引的首字符必须为'/' +// 如果存在以s为前缀的则返回大于0的值即其数量 +// 如果未找到则返回0 +//@receiver r *rax 接受者rax的指针 +//@param s string 待查找的前缀s +//@return num int 待查找前缀在rax树中存在的数量 +func (r *rax) Count(s string) (num int) { + if r.Empty() { + return 0 + } + if r.root == nil { + return 0 + } + if len(s) == 0 { + return 0 + } + if s[0] != '/' { + return 0 + } + //将s按'/'进行分割,并去掉第一个即去掉"",随后一次按照分层结果进行查找 + ss := strings.Split(s, "/")[1:] + r.mutex.Lock() + num = r.root.count(ss, 0) + r.mutex.Unlock() + return num +} + +//@title Find +//@description +// 以rax前缀基数树做接收者 +// 从rax中查找以s为索引的元素e,找到则返回e +// 用以查找的string索引的首字符必须为'/' +// 如果未找到则返回nil +//@receiver r *rax 接受者rax的指针 +//@param s string 待查找索引s +//@return ans interface{} 待查找索引所指向的元素 +func (r *rax) Find(s string) (e interface{}) { + if r.Empty() { + return nil + } + if len(s) == 0 { + return nil + } + if s[0] != '/' { + return nil + } + if r.root == nil { + return nil + } + //将s按'/'进行分割,并去掉第一个即去掉"",随后一次按照分层结果进行查找 + ss := strings.Split(s, "/")[1:] + r.mutex.Lock() + e = r.root.find(ss, 0) + r.mutex.Unlock() + return e +} diff --git a/goSTL/data_structure/trie/node.go b/goSTL/data_structure/trie/node.go index 1347567..a7a6543 100644 --- a/goSTL/data_structure/trie/node.go +++ b/goSTL/data_structure/trie/node.go @@ -109,12 +109,20 @@ func (n *node) insert(s string, p int, e interface{}) (b bool) { if idx == -1 { return false } + ok := true if n.son[idx] == nil { + //判断该子结点是否存在 n.son[idx] = newNode(nil) + ok = false } b = n.son[idx].insert(s, p+1, e) if b { n.num++ + } else { + if !ok { + //插入失败且该子节点为新建结点则需要删除该子结点 + n.son[idx] = nil + } } return b } @@ -177,14 +185,15 @@ func (n *node) delete(s string, p int) (num int) { return 0 } num = n.son[idx].delete(s, p+1) - if num>0 { - n.num-=num + if num > 0 { + n.num -= num if n.son[idx].num <= 0 { n.son[idx] = nil } } return num } + //@title count //@description // 以node单词查找树节点做接收者 diff --git a/goSTL/data_structure/trie/trie.go b/goSTL/data_structure/trie/trie.go index 4ac9e3e..90f38e1 100644 --- a/goSTL/data_structure/trie/trie.go +++ b/goSTL/data_structure/trie/trie.go @@ -117,6 +117,9 @@ func (t *trie) Clear() { //@param nil //@return b bool 该容器是空的吗? func (t *trie) Empty() (b bool) { + if t == nil { + return true + } return t.size == 0 } @@ -234,7 +237,7 @@ func (t *trie) Delete(s string) (num int) { // 如果未找到则返回0 //@receiver t *trie 接受者trie的指针 //@param s string 待查找的前缀s -//@return num int 待查找前缀在trie中存在的数量 +//@return num int 待查找前缀在trie树中存在的数量 func (t *trie) Count(s string) (num int) { if t == nil { return 0