309 lines
9.9 KiB
Go
Raw Normal View History

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
}