go radix tree

news2024/11/16 23:50:02

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)
}

Perfermence

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22765.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java 多线程()—— 线程同步=队列+锁

一、线程同步 队列 锁 同步就是多个线程同时访问一个资源。 那么如何实现&#xff1f; 队列锁。 想要访问同一资源的线程排成一个队列&#xff0c;按照排队的顺序访问。访问的时候加上一个锁&#xff08;参考卫生巾排队锁门&#xff09;&#xff0c;访问完释放锁。 二、 不…

gitblit 搭建本地 git 仓库

目录 一、简介 二、准备工作 1.安装Java 2.下载gitblit 3.创建资料目录 三、修改配置 1.git.repositoriesFolder 2.server.httpPort 3.server.httpBindInterface 4.installService.cmd 四、gitblit图标显示异常 结束 一、简介 Gitblit是一个用于管理&#xff0c;查…

数据结构与算法这么重要还不会?字节内部笔记来帮你轻松拿下!

对任何专业技术人员来说&#xff0c;理解数据结构都非常重要。作为软件开发者&#xff0c;我们要能够用编程语言和数据结构来解决问题。编程语言和数据结构是这些问题解决方案中不可或缺的一部分。如果选择了不恰当的数据结构&#xff0c;可能会影响所写程序的性能。因此&#…

VKL076-19*4点 超低功耗抗干扰LCD液晶段码显示屏驱动控制电路(IC/芯片),超低工作电流约7.5微安,多用于仪器仪表类,可提供FAE技术支持

产品品牌&#xff1a;永嘉微电/VINKA 产品型号&#xff1a;VKL076 封装形式&#xff1a;SSOP28 概述&#xff1a; VKL076 SSOP28是一个点阵式存储映射的LCD驱动器&#xff0c;可支持最大76点&#xff08;19SEGx4COM&#xff09;的 LCD屏。单片机可通过I2C接口配置显示参数和…

【Hack The Box】linux练习-- SwagShop

HTB 学习笔记 【Hack The Box】linux练习-- SwagShop &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月17日&#x1f334; &#x1…

Python 完美解决 Import “模块“ could not be resolved ...

vscode 中 python 提示警告错误&#xff0c;但是还是能跑起来代码&#xff1a; Import "playwright.sync_api" could not be resolved Pylance reportMissingImports 原因可能有两个&#xff1a; 1、未下载此包&#xff0c;打开命令行&#xff0c;输入 $ pip list&a…

problem B.Genshin Impact(2022合肥icpc)

题意&#xff1a;对目标持续施法&#xff0c;法术是每隔y秒让目标开始持续燃烧x秒&#xff0c;每次施法的概率是1/p 求燃烧时间比上总时间的期望值 &#xff08;题面是laji&#xff09; 思路&#xff1a;我们把总时间看成许多y段 当x<y的时候&#xff0c;只有一种情况就…

JVM虚拟机(整体架构、类文件结构)我来了~~~

虚拟机 1.1 发展历程 1.1.1 java往事 ​ Java诞生在一群懒惰、急躁而傲慢的程序天才之中。 ​ 1990年12月&#xff0c;Sun的工程师Patrick Naughton被当时糟糕的Sun C工具折磨的快疯了。他大声抱怨&#xff0c;并威胁要离开Sun转投当时在Steve Jobs领导之下的NeXT公司。领导…

CRGDFPASSC,CAS号:166184-23-2

CRGDFPASSC是一种含环rgd的十肽&#xff0c;与血小板表面的纤维蛋白原受体结合。在5号位置用Phe取代Ser的类似物作为血小板聚集抑制剂&#xff0c;其活性是CRGDSPASSC的3倍(IC₅₀ 2.5M)。 编号: 130659中文名称: CRGDFPASSC英文名: CRGDFPASSCCAS号: 166184-23-2单字母: CRGDF…

方法2—并行数据流转换为一种特殊串行数据流模块的设计,

并行数据流转换为一种特殊串行数据流模块的设计&#xff0c;设计两个可综合的电路模块1&#xff0c;第一个可综合模块&#xff0c;M1。2&#xff0c;描述M2模块3&#xff0c;描述M0模块的Verilog代码4&#xff0c;描述顶层模块5&#xff0c;电路生成的门级网表&#xff0c;netl…

【第五部分 | JS WebAPI】1:WebAPIs概述、网页元素的获取、事件

目录 | 概述 | 文档、元素、节点的概念 | 获取元素 根据ID获取 根据标签名获取 通过HTML5新增方法获取 特殊元素获取&#xff08;body html&#xff09; | 事件基础 事件三要素 点击事件 光标获得/失去焦点事件 [ 更多其它事件 ] 刷新网页自动执行某些事件 | 概述 …

Alibaba内部首发“面试百宝书+超全算法面试手册”PDF版下载

面试你打算要多高的薪资&#xff1f; 第一份工作的薪资水平就是你的薪资起点&#xff0c;如果你拿到的第一份薪水远高于其他人&#xff0c;那么你在未来涨薪路上就会省很多力。 想刚开始工作就拥有高薪&#xff0c;那就需要抬高自己的“身价”&#xff0c;提升自己的工作能力…

[附源码]java毕业设计-线上摄影平台系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Blob和ArrayBuffer和File

Blob Blob对象表示一个不可变、原始数据的类似文件的对象。Blob 表示的不一定是JavaScript原生格式的数据。 Represents a “Binary Large Object”, meaning a file-like object of immutable, raw data。 type BufferSource ArrayBufferView | ArrayBuffer; type BlobPart…

微前端——single-spa源码学习

前言 本来是想直接去学习下qiankun的源码&#xff0c;但是qiankun是基于single-spa做的二次封装&#xff0c;通过解决了single-spa的一些弊端和不足来帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。 所以我们应该先对single-spa有一个全面的认识和了解&#xff0c…

看了就能懂的NIO使用深入详解

NIO概述 NIO介绍 传统IO流(java.io):读写操作结束前,处于线性阻塞,代码简单,安全,性能低 NIO&#xff1a;支持非阻塞式编程,性能更有优势,但代码编写较为复杂。 概念理解 同步(synchronous):一条线程执行期间,其他线程就只能等待。 异步(asynchronous):一条线程在执行…

Java基础深化和提高-------多线程与并发编程

目录 多线程与并发编程 多线程介绍 什么是程序&#xff1f; 什么是进程? 什么是线程&#xff1f; 进程、线程的区别 什么是并发 线程和方法的执行特点 方法的执行特点 线程的执行特点 什么是主线程以及子线程 主线程 子线程 线程的创建 通过继承Thread类实现多线程 通过Ru…

暴力美学,拒绝平庸,Alibab开源内部神仙级“K8S核心笔记”下载

各大互联网巨头在技术战略层面&#xff0c;都把云原生列为了主要发展方向。以阿里巴巴为例&#xff0c;他们技术老大说&#xff0c;云原生是云计算释放红利的最短路径&#xff0c;也是企业数字化的最短路径。 现在云原生工程师、Kubernetes 工程师工资都特别高&#xff0c;并且…

大厂光环下的功能测试,出去面试自动化一问三不知

在一家公司待久了技术能力反而变弱了&#xff0c;原来的许多知识都会慢慢遗忘&#xff0c;这种情况并不少见。 一个京东员工发帖吐槽&#xff1a;感觉在大厂快待废了&#xff0c;出去面试问自己接口环境搭建、pytest测试框架&#xff0c;自己做点工太久都忘记了。平时用的时候…

【BLE】蓝牙数据速率

【BLE】蓝牙数据速率 理论速度 物理层 未编码PHY&#xff0c;每位数据使用1个符号表示 1Mbps&#xff08;LE 1M PHY&#xff09; 2Mbps&#xff08;LE 2M PHY&#xff09; 编码PHY 500Kbps&#xff08;S2&#xff09; 125Kbps&#xff08;S8&#xff09; 1Mbps指的是每…