• go radix tree


    Radix Tree在这里插入图片描述

    Search

    在这里插入图片描述

    Insert

    在这里插入图片描述
    Insert ‘water’ at the root
    在这里插入图片描述
    Insert ‘slower’ while keeping ‘slow’
    在这里插入图片描述
    Insert ‘test’ which is a prefix of ‘tester’
    在这里插入图片描述
    Insert ‘team’ while splitting ‘test’ and creating a new edge label ‘st’
    在这里插入图片描述
    Insert ‘toast’ while splitting ‘te’ and moving previous strings a level lower

    Implement

    package main
    
    import (
    	"fmt"
    	"math/rand"
    	"strings"
    	"time"
    )
    
    type node[T any] struct {
    	children      []*node[T]
    	childrenIndex []byte
    	key           string
    	value         T
    	hasValue      bool
    }
    
    func (n *node[T]) get(key string) *node[T] {
    	if n == nil {
    		return nil
    	}
    	if l := getLongestPublicPrefixIndex(key, n.key); l == len(n.key) { //与node的key全匹配
    		key = key[l:]
    		if len(key) == 0 {
    			return n
    		}
    		for i := 0; i < len(n.childrenIndex); i++ {
    			if n.childrenIndex[i] == key[0] {
    				return n.children[i].get(key)
    			}
    		}
    	}
    	return nil
    }
    
    /*
    To delete a string x from a tree, we first locate the leaf representing x.
    Then, assuming x exists, we remove the corresponding leaf node.
    If the parent of our leaf node has only one other child,
    then that child's incoming label is appended to the parent's incoming label and the child is removed.
    */
    func (n *node[T]) delete(key string) (oldV T) {
    	if n == nil {
    		return
    	}
    	if l := getLongestPublicPrefixIndex(key, n.key); l == len(n.key) {
    		key = key[l:]
    		if len(key) == 0 {
    			oldV = n.value
    			if len(n.children) == 0 { // 是叶子节点,防止不经过回溯,不被删除
    				*n = *new(node[T])
    			}
    			n.hasValue = false // 非叶子节点 逻辑删除
    			return
    		}
    
    		for i := 0; i < len(n.childrenIndex); i++ {
    			if n.childrenIndex[i] == key[0] {
    				oldV = n.children[i].delete(key)
    				//回溯,在parent的视角
    				if len(n.children[i].children) == 0 && !n.children[i].hasValue { //是叶子节点,被逻辑删除,进行物理删除
    					n.children = append(n.children[:i], n.children[i+1:]...)
    					n.childrenIndex = append(n.childrenIndex[:i], n.childrenIndex[i+1:]...)
    				}
    				if len(n.children) == 1 && n.hasValue == false { // 清理工作,向上扫一遍,有一个孩子的向上合并
    					n.children[0].key = n.key + n.children[0].key
    					*n = *n.children[0]
    				}
    				return
    			}
    		}
    	}
    	return
    }
    
    func (n *node[T]) set(key string, value T) (oldValue T) {
    	if l := getLongestPublicPrefixIndex(key, n.key); l == len(n.key) {
    		key = key[l:]
    		if len(key) == 0 {
    			oldValue, n.value = n.value, value
    			n.hasValue = true
    			return
    		}
    		for i := 0; i < len(n.childrenIndex); i++ {
    			if n.childrenIndex[i] == key[0] {
    				return n.children[i].set(key, value)
    			}
    		}
    	} else {
    		prefix, suffix := n.key[:l], n.key[l:]
    		child := &node[T]{
    			children:      n.children,
    			childrenIndex: n.childrenIndex,
    			key:           suffix,
    			value:         n.value,
    			hasValue:      n.hasValue,
    		}
    
    		*n = node[T]{
    			key:           prefix,
    			children:      []*node[T]{child},
    			childrenIndex: []byte{child.key[0]},
    		}
    		key = key[l:]
    		if len(key) == 0 {
    			oldValue, n.value = n.value, value
    			n.hasValue = true
    			return
    		}
    	}
    	n.children = append(n.children, &node[T]{key: key, value: value, hasValue: true})
    	n.childrenIndex = append(n.childrenIndex, key[0])
    	return
    }
    
    type RadixTreeMap[T any] struct {
    	root *node[T]
    }
    
    func NewRadixTreeMap[T any]() *RadixTreeMap[T] {
    	return &RadixTreeMap[T]{}
    }
    
    func (t *RadixTreeMap[T]) Get(key string) T {
    	if len(key) == 0 {
    		return *new(T)
    	}
    	n := t.root.get(key)
    	if n != nil {
    		return n.value
    	}
    	return *new(T)
    }
    
    func (t *RadixTreeMap[T]) Delete(key string) T {
    	return t.root.delete(key)
    }
    
    func (t *RadixTreeMap[T]) Set(key string, value T) (oldValue T) {
    	if len(key) == 0 {
    		return
    	}
    	if t.root == nil {
    		t.root = &node[T]{key: key, value: value, hasValue: true}
    		return
    	}
    	return t.root.set(key, value)
    }
    
    func min(a, b int) int {
    	if a < b {
    		return a
    	}
    	return b
    }
    
    func getLongestPublicPrefixIndex(str1, str2 string) int {
    	minLen := min(len(str1), len(str2))
    	index := 0
    	for index < minLen && str1[index] == str2[index] {
    		index++
    	}
    	return index
    }
    
    //----------------------- for test -----------------------
    
    func printTree[T any](root *node[T], weight int) {
    	if root == nil {
    		return
    	}
    
    	if weight <= 0 {
    		weight = 1
    	}
    	fmt.Println(strings.Repeat("->", weight))
    	if len(root.key) != 0 {
    		fmt.Println(string(root.key), ":", root.value)
    	}
    	for i := 0; i < len(root.children); i++ {
    		printTree(root.children[i], weight+1)
    	}
    	fmt.Println(strings.Repeat("<-", weight))
    }
    
    func generateStringMap(totalCount, eachLength int) (map[string]string, int64) {
    	b := strings.Builder{}
    	for i := 'a'; i <= 'z'; i++ {
    		b.WriteString(string(i))
    		b.WriteString(string(i - ' '))
    	}
    	b.WriteString("0123456789")
    	dic := b.String()
    
    	length := len(dic)
    
    	mp := map[string]string{}
    	total := int64(0)
    	for i := 0; i < totalCount; i++ {
    		b1 := strings.Builder{}
    		//l := rand.Intn(eachLength) + 1
    		for j := 0; j < eachLength; j++ {
    			b1.WriteString(string(dic[rand.Intn(length)]))
    		}
    		mp[b1.String()] = b1.String()
    		total += int64(len(b1.String()))
    	}
    	return mp, total
    }
    
    func test() {
    	start := time.Now()
    	mp, total := generateStringMap(1000000, 1024)
    	fmt.Println("生成样本用时:", time.Since(start))
    	fmt.Println("样本量:", len(mp))
    	fmt.Println("样本size:", total*2)
    	start = time.Now()
    	t := NewRadixTreeMap[string]()
    	for k, v := range mp {
    		if res := t.Set(k, v); res != "" {
    			panic("Set result is not nil" + string(res) + v)
    		}
    	}
    	fmt.Println("SetAll 用时:", time.Since(start))
    
    	start = time.Now()
    	for k, v := range mp {
    		if res := t.Get(k); res != v {
    			panic("Get not equal " + string(res) + v)
    		}
    	}
    	fmt.Println("GetAll 用时:", time.Since(start))
    
    	start = time.Now()
    	for k, v := range mp {
    		if res := t.Delete(k); res != v {
    			panic("Delete not equal " + string(res) + v)
    		}
    	}
    	fmt.Println("DeleteAll 用时:", time.Since(start))
    
    	printTree(t.root, 5)
    	fmt.Println(t.root)
    }
    
    func main() {
    	test()
    	return
    	t := NewRadixTreeMap[string]()
    	t.Set("b", "b")
    	t.Set("f", "f")
    	t.Set("a", "a")
    	t.Set("abc", "abc")
    	t.Set("ab", "ab")
    	t.Set("abd", "abd")
    	t.Set("acd", "acd")
    
    	fmt.Println(t.Delete("b"))
    	fmt.Println(t.Delete("a"))
    	fmt.Println(t.Delete("ab"))
    	fmt.Println(t.Delete("abc"))
    	fmt.Println(t.Delete("acd"))
    	fmt.Println(t.Delete("abd"))
    	fmt.Println(t.Delete("c"))
    	fmt.Println(t.Delete("f"))
    
    	printTree(t.root, 5)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269

    Perfermence

    CPU:i5-7200U,双核4线程

    生成样本用时: 42.0999128s
    样本量: 1000000
    样本size: 2048000000
    SetAll 用时: 1.5978911s
    GetAll 用时: 2.3042968s
    DeleteAll 用时: 2.0477653s

  • 相关阅读:
    一个注解搞定责任链,学还是不学?
    JVM学习(宋红康)之运行时数据区之虚拟机栈中栈帧的动态链接及方法调用
    【hive】异常日期查找
    电脑重装系统后Win11用户账户控制设置怎么取消
    Redis常用命令集
    网络安全(黑客)-0基础小白自学
    Linux系统中查看NextJS程序的CPU、内存占用情况
    修改Mysql数据库的用户名和密码【详细】
    flask中使用redis做缓存
    nbcio-boot移植到若依ruoyi-nbcio平台里一formdesigner部分(一)
  • 原文地址:https://blog.csdn.net/dawnto/article/details/127962516