• 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

  • 相关阅读:
    盘点一个pandas.merge的问题
    便捷安装机房常用python库
    【一起学前端:HTML5+CSS3】01-前端简介
    Integer、Long 等包装类 == 值判断、地址判断与缓存
    《C和指针》笔记31:多维数组的数组名、指向多维数组的指针、作为函数参数的多维数组
    Linux中每当执行‘mount’命令(或其他命令)时,自动激活执行脚本:输入密码,才可以执行mount
    c语言练习44:深入理解strstr
    RocketMq存储设计——MappedFile
    8086 汇编笔记(十):标志寄存器
    大数据架构设计理论与实践
  • 原文地址:https://blog.csdn.net/dawnto/article/details/127962516