diff --git a/goSTL/data_structure/radix/node.go b/goSTL/data_structure/radix/node.go new file mode 100644 index 0000000..4921f10 --- /dev/null +++ b/goSTL/data_structure/radix/node.go @@ -0,0 +1,308 @@ +package radix + +import ( + "strings" +) + +//@Title radix +//@Description +// 前缀基数树的节点 +// 可通过节点的分叉对string进行查找 +// 增添string时候需要增删结点,同时将结点内置的map中增删对应的string即可 +// 当string到终点时存储元素 + +//node树节点结构体 +//该节点是radix的树节点 +//结点存储到此时的string的前缀数量 +//son存储其下属分叉的子结点指针 +//该节点同时存储其元素 +type node struct { + pattern string //到终点时不为"",其他都为"" + part string //以当前结点的string内容 + num int //以当前结点为前缀的数量 + sons map[string]*node //该结点下属结点的指针 + fuzzy bool //模糊匹配?该结点首字符为':'或'*'为模糊匹配 +} + +//@title newNode +//@description +// 新建一个前缀基数树节点并返回 +// 将传入的元素e作为该节点的承载元素 +//@receiver nil +//@param name string 该节点的名字,即其对应的string +//@return n *node 新建的单词查找树节点的指针 +func newNode(part string) (n *node) { + fuzzy := false + if len(part) > 0 { + fuzzy = part[0] == ':' || part[0] == '*' + } + return &node{ + pattern: "", + part: part, + num: 0, + sons: make(map[string]*node), + fuzzy: fuzzy, + } +} + +//@title analysis +//@description +// 将string按'/'进行分段解析 +// 为""部分直接舍弃,返回解析结果 +// 同时按规则重组用以解析是string并返回 +//@receiver nil +//@param s string 待解析的string +//@return ss []string 按'/'进行分层解析后的结果 +//@return newS string 按符合规则解析结果重组后的s +func analysis(s string) (ss []string, newS string) { + vs := strings.Split(s, "/") + ss = make([]string, 0) + newS = "/" + for _, item := range vs { + if item != "" { + ss = append(ss, item) + newS = newS + "/" + item + if item[0] == '*' { + break + } + } + } + return ss, newS +} + +//@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.pattern != "" { + es = append(es, s+n.part) + } + for _, son := range n.sons { + es = append(es, son.inOrder(s+n.part+"/")...) + } + return es +} + +//@title insert +//@description +// 以node前缀基数树节点做接收者 +// 从n节点中继续插入以s为索引的元素e,且当前抵达的string位置为p +// 当到达s终点时进行插入,如果此时node承载了string则插入失败,否则成功 +// 当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可 +// 当插入失败且对应子结点为新建节点时则需要删除该子结点 +//@receiver n *node 接受者node的指针 +//@param pattern string 待插入的string整体 +//@param ss []string 待删除元素的索引s的按'/'进行分层的索引集合 +//@param p int 索引当前抵达的位置 +//@return b bool 是否插入成功? +func (n *node) insert(pattern string, ss []string, p int) (b bool) { + if p == len(ss) { + if n.pattern != "" { + //该节点承载了string + return false + } + //成功插入 + n.pattern = pattern + n.num++ + return true + } + //找到该层的string + s := ss[p] + //从其子结点的map中找到对应的方向 + son, ok := n.sons[s] + if !ok { + //不存在,新建并放入map中 + son = newNode(s) + n.sons[s] = son + } + //从子结点对应方向继续插入 + b = son.insert(pattern, ss, p+1) + if b { + n.num++ + } else { + if !ok { + //插入失败且该子节点为新建结点则需要删除该子结点 + delete(n.sons, s) + } + } + return b +} + +//@title erase +//@description +// 以node前缀基数树节点做接收者 +// 从n节点中继续删除以s为索引的元素e,且当前抵达的string位置为p +// 当到达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.pattern != "" { + //该结点承载是string是,删除成功 + n.pattern = "" + 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 +// 当到达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 { + son.num -= num + if son.num <= 0 { + //删除后子结点的num<=0即该节点无后续存储元素,可以销毁 + delete(n.sons, s) + } + } + return num +} + +//@title count +//@description +// 以node前缀基数树节点做接收者 +// 从n节点中继续查找以s为前缀索引的元素e,且当前抵达的string位置为p +// 当到达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 mate +//@description +// 以node前缀基数树节点做接收者 +// 先从radix树的根节点开始找到第一个可以满足该模糊匹配方案的string结点 +// 随后将s和结点的pattern进行模糊映射,将模糊查找的值和匹配值进行映射并返回即可 +// 若该结点未找到则直接返回nil和false即可 +//@receiver n *node 接受者node的指针 +//@param ss []string 待删除元素的索引s的按'/'进行分层的索引集合 +//@param p int 索引当前抵达的位置 +//@return m map[string]string s从结点中利用模糊匹配到的所有key和value的映射 +//@return ok bool 匹配成功? +func (n *node) mate(s string, p int) (m map[string]string, ok bool) { + //解析url + searchParts, _ := analysis(s) + //从该请求类型中寻找对应的路由结点 + q := n.find(searchParts, 0) + if q != nil { + //解析该结点的pattern + parts, _ := analysis(q.pattern) + //动态参数映射表 + params := make(map[string]string) + for index, part := range parts { + if part[0] == ':' { + //动态匹配,将参数名和参数内容的映射放入映射表内 + params[part[1:]] = searchParts[index] + } + if part[0] == '*' && len(part) > 1 { + //通配符,将后续所有内容全部添加到映射表内同时结束遍历 + params[part[1:]] = strings.Join(searchParts[index:], "/") + break + } + } + return params, true + } + return nil, false +} + +//@title find +//@description +// 以node前缀基数树节点做接收者 +// 从radix树的根节点开始找到第一个可以满足该模糊匹配方案的string结点 +// 若该结点未找到则直接返回nil +//@receiver n *node 接受者node的指针 +//@param ss []string 待删除元素的索引s的按'/'进行分层的索引集合 +//@param p int 索引当前抵达的位置 +//@return m map[string]string s从结点中利用模糊匹配到的所有key和value的映射 +//@return ok bool 匹配成功? +func (n *node) find(parts []string, height int) (q *node) { + //根据长度和局部string的首字符进行判断 + if len(parts) == height || strings.HasPrefix(n.part, "*") { + if n.pattern == "" { + //匹配失败,该结点处无匹配的信息 + return nil + } + //匹配成功,返回该结点 + return n + } + //从该结点的所有子结点中查找可用于递归查找的结点 + //当局部string信息和当前层string相同时可用于递归查找 + //当该子结点是动态匹配时也可以用于递归查找 + part := parts[height] + //从所有子结点中找到可用于递归查找的结点 + children := make([]*node, 0, 0) + for _, child := range n.sons { + if child.part == part || child.fuzzy { + //局部string相同或动态匹配 + children = append(children, child) + } + } + for _, child := range children { + //递归查询,并根据结果进行判断 + result := child.find(parts, height+1) + if result != nil { + //存在一个满足时就可以返回 + return result + } + } + return nil +} diff --git a/goSTL/data_structure/radix/radix.go b/goSTL/data_structure/radix/radix.go new file mode 100644 index 0000000..f92a417 --- /dev/null +++ b/goSTL/data_structure/radix/radix.go @@ -0,0 +1,277 @@ +package radix + +//@Title radix +//@Description +// 前缀基数树-radix +// 以多叉树的形式实现,根据'/'进行string分割,将分割后的string数组进行段存储 +// 不存储其他元素,仅对string进行分段存储和模糊匹配 +// 使用互斥锁实现并发控制 + +import ( + "github.com/hlccd/goSTL/utils/iterator" + "sync" +) + +//radix前缀基数树结构体 +//该实例存储前缀基数树的根节点 +//同时保存该树已经存储了多少个元素 +type radix struct { + root *node //前缀基数树的根节点指针 + size int //当前已存放的元素数量 + mutex sync.Mutex //并发控制锁 +} + +//radix前缀基数树容器接口 +//存放了radix前缀基数树可使用的函数 +//对应函数介绍见下方 +type radixer interface { + Iterator() (i *Iterator.Iterator) //返回包含该radix的所有string + Size() (num int) //返回该radix中保存的元素个数 + Clear() //清空该radix + Empty() (b bool) //判断该radix是否为空 + Insert(s string) (b bool) //向radix中插入string + Erase(s string) (b bool) //从radix中删除string + Delete(s string) (num int) //从radix中删除以s为前缀的所有string + Count(s string) (num int) //从radix中寻找以s为前缀的string单词数 + Mate(s string) (m map[string]string, ok bool) //利用radix树中的string对s进行模糊匹配,':'可模糊匹配该层,'*'可模糊匹配后面所有 +} + +//@title New +//@description +// 新建一个radix前缀基数树容器并返回 +// 初始根节点为nil +//@receiver nil +//@param nil +//@return r *radix 新建的radix指针 +func New() (r *radix) { + return &radix{ + root: newNode(""), + size: 0, + mutex: sync.Mutex{}, + } +} + +//@title Iterator +//@description +// 以radix前缀基数树做接收者 +// 将该radix中所有存放的string放入迭代器中并返回 +//@receiver r *radix 接受者radix的指针 +//@param nil +//@return i *iterator.Iterator 新建的Iterator迭代器指针 +func (r *radix) 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 +// 以radix前缀基数树做接收者 +// 返回该容器当前含有元素的数量 +// 如果容器为nil返回0 +//@receiver r *radix 接受者radix的指针 +//@param nil +//@return num int 容器中实际使用元素所占空间大小 +func (r *radix) Size() (num int) { + if r == nil { + return 0 + } + if r.root == nil { + return 0 + } + return r.size +} + +//@title Clear +//@description +// 以radix前缀基数树做接收者 +// 将该容器中所承载的元素清空 +// 将该容器的size置0 +//@receiver r *radix 接受者radix的指针 +//@param nil +//@return nil +func (r *radix) Clear() { + if r == nil { + return + } + r.mutex.Lock() + r.root = newNode("") + r.size = 0 + r.mutex.Unlock() +} + +//@title Empty +//@description +// 以radix前缀基数树做接收者 +// 判断该radix是否含有元素 +// 如果含有元素则不为空,返回false +// 如果不含有元素则说明为空,返回true +// 如果容器不存在,返回true +//@receiver r *radix 接受者radix的指针 +//@param nil +//@return b bool 该容器是空的吗? +func (r *radix) Empty() (b bool) { + if r == nil { + return true + } + return r.size == 0 +} + +//@title Insert +//@description +// 以radix前缀基数树做接收者 +// 向radix插入string +// 将对string进行解析,按'/'进行分层,':'为首则为模糊匹配该层,'*'为首则为模糊匹配后面所有 +// 已经存在则无法重复插入 +//@receiver r *radix 接受者radix的指针 +//@param s string 待插入string +//@return b bool 添加成功? +func (r *radix) Insert(s string) (b bool) { + if r == nil { + return false + } + //解析s并按规则重构s + ss, s := analysis(s) + r.mutex.Lock() + if r.root == nil { + //避免根节点为nil + r.root = newNode("") + } + //从根节点开始插入 + b = r.root.insert(s, ss, 0) + if b { + //插入成功,size+1 + r.size++ + } + r.mutex.Unlock() + return b +} + +//@title Erase +//@description +// 以radix前缀基数树做接收者 +// 从radix树中删除元素string +//@receiver r *radix 接受者radix的指针 +//@param s string 待删除的string +//@return b bool 删除成功? +func (r *radix) Erase(s string) (b bool) { + if r.Empty() { + return false + } + if len(s) == 0 { + return false + } + if r.root == nil { + //根节点为nil即无法删除 + return false + } + //解析s并按规则重构s + ss, _ := analysis(s) + 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 +// 以radix前缀基数树做接收者 +// 从radix树中删除以s为前缀的所有string +//@receiver r *radix 接受者radix的指针 +//@param s string 待删除string的前缀 +//@return num int 被删除的元素的数量 +func (r *radix) Delete(s string) (num int) { + if r.Empty() { + return 0 + } + if len(s) == 0 { + return 0 + } + if r.root == nil { + return 0 + } + //解析s并按规则重构s + ss, _ := analysis(s) + 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 +// 以radix前缀基数树做接收者 +// 从radix中查找以s为前缀的所有string的个数 +// 如果存在以s为前缀的则返回大于0的值即其数量 +// 如果未找到则返回0 +//@receiver r *radix 接受者radix的指针 +//@param s string 待查找的前缀s +//@return num int 待查找前缀在radix树中存在的数量 +func (r *radix) Count(s string) (num int) { + if r.Empty() { + return 0 + } + if r.root == nil { + return 0 + } + if len(s) == 0 { + return 0 + } + //解析s并按规则重构s + ss, _ := analysis(s) + r.mutex.Lock() + num = r.root.count(ss, 0) + r.mutex.Unlock() + return num +} + +//@title Mate +//@description +// 以radix前缀基数树做接收者 +// 从radix中查找以s为信息的第一个可以模糊匹配到的key和value的映射表 +// key是radix树中的段名,value是s中的段名 +// 如果未找到则返回nil和false +// 否则返回一个映射表和true +//@receiver r *radix 接受者radix的指针 +//@param s string 待查找的信息s +//@return m map[string]string s从前缀基数树中利用模糊匹配到的所有key和value的映射 +//@return ok bool 匹配成功? +func (r *radix) Mate(s string) (m map[string]string, ok bool) { + if r.Empty() { + return nil, false + } + if len(s) == 0 { + return nil, false + } + if r.root == nil { + return nil, false + } + //将s按'/'进行分割,并去掉第一个即去掉"",随后一次按照分层结果进行查找 + r.mutex.Lock() + m, ok = r.root.mate(s, 0) + r.mutex.Unlock() + return m, ok +}