当前位置: 首页 > news >正文

跳表的学习记录

跳表(Skip List)是一种数据结构,它通过在多个层次上添加额外的前向指针来提高有序数据的搜索效率。跳表与其他常见的有序数据结构(如二叉搜索树、平衡树如AVL树和红黑树、B树等)相比,具有其独特的优缺点:

跳表的优点

简单性: 跳表的算法和数据结构相对简单,容易理解和实现。与平衡树的复杂旋转和重新平衡相比,跳表的维护成本较低。
高效的搜索操作: 跳表可以提供接近二叉搜索树的搜索性能,平均时间复杂度为 O(logn)。
灵活的插入和删除: 跳表在插入和删除操作时,只需要调整相邻的指针,而不需要像在平衡树中那样复杂的重新平衡。
有序性: 与哈希表不同,跳表维护了元素的有序性,使得范围查询等操作更加高效。

跳表的缺点

空间消耗: 由于跳表在每个节点中存储多个指针,它通常会消耗比二叉树更多的内存空间。
随机性: 跳表的性能部分依赖于随机化过程,这可能导致性能的不稳定性。虽然在平均情况下表现良好,但最坏情况的表现可能不如某些确定性结构。
缓存不友好: 由于跳表的节点可能分布在内存的不同位置,它可能不如紧凑存储的数据结构(如数组)那样对缓存友好。

举个例子

跳表的 put 操作
想象一下你在一个有多层的购物中心里。每一层都有很多商店,而且每层都有楼梯和电梯连接到其他层。现在,你要在这个购物中心里开一家新店。
put 操作就像是在购物中心里找一个合适的位置来开设你的店铺。
选择楼层: 你首先决定你的店铺要开在哪些楼层。这就像跳表中随机决定节点应该在哪些层上有链接。
找到位置: 接着,你从最高层开始,寻找开设店铺的位置。你会经过每一家店,直到找到合适的空位。这就像在跳表中,从最高层开始向右搜索,直到找到合适的插入点。
开设店铺: 一旦找到位置,你就在那里开设你的店铺,并确保楼梯或电梯可以通到你的店铺。这在跳表中对应于将新节点插入到链表中,并更新相邻节点的指针。

跳表的 del 操作
现在,想象你决定关闭你在购物中心的某个店铺。
del 操作就像是在购物中心中关闭并移除你的店铺。
寻找店铺: 你首先需要找到你的店铺。这就像在跳表中寻找一个特定的节点。
关闭并移除: 一旦找到,你就关闭店铺,并且移除所有通向这家店铺的楼梯和电梯的指示标志。这在跳表中对应于移除一个节点,并更新所有指向该节点的指针,确保它们指向下一个正确的节点。
调整楼层: 如果你关闭的是最高层的唯一店铺,那么整个楼层可能会被关闭。这就像在跳表中,如果删除节点后某一层没有任何节点了,我们就降低跳表的高度。

代码实现

package mainimport ("fmt""math/rand""time"
)type node struct {next     []*nodekey, val int
}type SkipList struct {head *node
}func (s *SkipList) search(key int) *node {move := s.headfor level := len(s.head.next) - 1; level >= 0; level-- {for move.next[level] != nil && move.next[level].key < key {move = move.next[level]}if move.next[level] != nil && move.next[level].key == key {return move.next[level]}}return nil
}func (s *SkipList) Get(key int) (int, bool) {if searchNode := s.search(key); searchNode != nil {return searchNode.val, true}return -1, false
}// Adjust the roll method to control the height distribution
func (s *SkipList) roll() int {level := 0for rand.Float32() < 0.5 { // Adjust the probability as neededlevel++}return level
}func (s *SkipList) Put(key, val int) {if search := s.search(key); search != nil {search.val = valreturn}level := s.roll()for len(s.head.next)-1 < level {s.head.next = append(s.head.next, nil)}newNode := node{next: make([]*node, level+1),key:  key,val:  val,}move := s.headfor l := level; l >= 0; l-- {for move.next[l] != nil && move.next[l].key < key {move = move.next[l]}newNode.next[l] = move.next[l]move.next[l] = &newNode}
}func (s *SkipList) Del(key int) {if _node := s.search(key); _node == nil {return}move := s.headfor level := len(s.head.next) - 1; level >= 0; level-- {for move.next[level] != nil && move.next[level].key < key {move = move.next[level]}if move.next[level] != nil && move.next[level].key == key {move.next[level] = move.next[level].next[level]}}for len(s.head.next) > 1 && s.head.next[len(s.head.next)-1] == nil {s.head.next = s.head.next[:len(s.head.next)-1]}
}func (s *SkipList) ceiling(target int) *node {move := s.headfor level := len(s.head.next) - 1; level >= 0; level-- {for move.next[level] != nil && move.next[level].key < target {move = move.next[level]}if move.next[level] != nil && move.next[level].key == target {return move.next[level]}}return move.next[0]
}func (s *SkipList) Range(start, end int) [][2]int {ceilNode := s.ceiling(start)if ceilNode == nil {return [][2]int{}}var res [][2]intfor move := ceilNode; move != nil && move.key <= end; move = move.next[0] {res = append(res, [2]int{move.key, move.val})}return res
}func main() {sl := createTestSkipList()// Test Insertionfmt.Println("Testing Insertion...")sl.Put(3, 30)sl.Put(1, 10)sl.Put(2, 20)fmt.Println("Insertion Passed")// Test Searchfmt.Println("Testing Search...")if val, found := sl.Get(2); !found || val != 20 {fmt.Printf("Search Failed: expected 20, got %d\n", val)} else {fmt.Println("Search Passed")}// Test Deletionfmt.Println("Testing Deletion...")sl.Del(1)if _, found := sl.Get(1); found {fmt.Println("Deletion Failed: 1 should not exist")} else {fmt.Println("Deletion Passed")}// Test Ceiling Functionfmt.Println("Testing Ceiling Function...")ceilNode := sl.ceiling(2)if ceilNode == nil || ceilNode.key != 2 {fmt.Printf("Ceiling Failed: expected key 2, got %d\n", ceilNode.key)} else {fmt.Println("Ceiling Passed")}// Test Range Queryfmt.Println("Testing Range Query...")expected := [][2]int{{2, 20}, {3, 30}}result := sl.Range(2, 3)if len(result) != len(expected) {fmt.Printf("Range Failed: expected length %d, got %d\n", len(expected), len(result))} else {allMatch := truefor i, pair := range expected {if result[i] != pair {fmt.Printf("Range Failed at index %d: expected %v, got %v\n", i, pair, result[i])allMatch = falsebreak}}if allMatch {fmt.Println("Range Passed")}}
}func createTestSkipList() *SkipList {rand.Seed(time.Now().UnixNano())sl := SkipList{head: &node{next: make([]*node, 1)}}return &sl
}

相关文章:

跳表的学习记录

跳表&#xff08;Skip List&#xff09;是一种数据结构&#xff0c;它通过在多个层次上添加额外的前向指针来提高有序数据的搜索效率。跳表与其他常见的有序数据结构&#xff08;如二叉搜索树、平衡树如AVL树和红黑树、B树等&#xff09;相比&#xff0c;具有其独特的优缺点&am…...

电子学会C/C++编程等级考试2022年09月(二级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:统计误差范围内的数 统计一个整数序列中与指定数字m误差范围小于等于X的数的个数。 时间限制:5000 内存限制:65536输入 输入包含三行: 第一行为N,表示整数序列的长度(N <= 100); 第二行为N个整数,整数之间以一个空格分…...

如何使用nginx部署静态资源

Nginx可以作为静态web服务器来部署静态资源&#xff0c;这个静态资源是指在服务端真实存在&#xff0c;并且能够直接展示的一些文件数据&#xff0c;比如常见的静态资源有html页面、css文件、js文件、图片、视频、音频等资源相对于Tomcat服务器来说&#xff0c;Nginx处理静态资…...

lua的gc原理

lua垃圾回收(Garbage Collect)是lua中一个比较重要的部分。由于lua源码版本变迁&#xff0c;目前大多数有关这个方面的文章都还是基于lua5.1版本&#xff0c;有一定的滞后性。因此本文通过参考当前的5.3.4版本的Lua源码&#xff0c;希望对Lua的GC算法有一个较为详尽的探讨。 L…...

redis作为缓存详解

目录 前言&#xff1a; 为什么说关系型数据库性能不高 如何提高MySQL并发量 缓存更新策略 定期更新 实时更新 内存淘汰策略 Redis内置的淘汰策略 缓存常见问题 缓存预热 缓存穿透 缓存雪崩 缓存击穿 前言&#xff1a; 对于缓存的理解&#xff0c;缓存目的就是为了…...

231127 刷题日报

这周值班。。多少写道题吧&#xff0c;保持每天的手感。老婆给买了lubuladong纸质书&#xff0c;加油卷。 1. 131. 分割回文串 写个这个吧&#xff0c;钉在耻辱柱上的题。 为啥没写出来&#xff1a; 1. 递归树没画对 把树枝只看做是1个字母&#xff0c;而且不清楚树枝和节点…...

【Linux】vim-多模式的文本编辑器

本篇文章内容和干货较多&#xff0c;希望对大家有所帮助&#x1f44d; 目录 一、vim的介绍 1.1 vi 与 vim的概念1.2 Vim 和 Vi 的一些对比 二、vim 模式之间的切换 2.1 进入vim2.2 [正常模式]切换到[插入模式]2.3 [插入模式]切换至[正常模式]2.4 [正常模式]切换至[底行模式…...

Ubuntu 启用 root 用户

在启用 root 用户之前&#xff0c;我们先来了解一下&#xff0c; ubuntu 命令的组成。 打开 ubuntu 的终端&#xff0c;现在的命令行是由 topeetubuntu:~$ 这几个字母组成&#xff0c;那么这几个字母都代表 什么意思呢&#xff1f; topeet …...

手摸手Element-ui路由VueRoute

后端WebAPI准备 https://router.vuejs.org/zh/guide/ https://v3.router.vuejs.org/zh/installation.html <template><el-table:data"tableData"style"width: 100%":row-class-name"tableRowClassName"><!-- <el-table-colum…...

探究Kafka原理-5.Kafka设计原理和生产者原理解析

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44…...

浅谈C#在unity应用中的工厂模式

文章目录 前言简单工厂模式工厂方法模式抽象工厂模式Unity实战 前言 工厂模式是一种创建型设计模式&#xff0c;它提供了一种将对象的实例化过程封装起来的方法&#xff0c;使得客户端代码不必直接依赖于具体类。这有助于降低代码的耦合度&#xff0c;提高代码的可维护性和可扩…...

卷积神经网络(Inception-ResNet-v2)交通标志识别

文章目录 一、前言二、前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;2. 导入数据3. 查看数据 二、构建一个tf.data.Dataset1.加载数据2. 配置数据集 三、构建Inception-ResNet-v2网络1.自己搭建2.官方模型 五、设置动态学习率六、训练模型七、模型评…...

网易云音频数据如何爬取?

在当今数字化时代&#xff0c;音频数据的获取和处理变得越来越重要。本文将详细介绍如何使用Objective-C语言构建音频爬虫程序&#xff0c;以爬取网易云音乐为案例。我们将从Objective-C的基础知识开始&#xff0c;逐步深入到爬取思路分析、构建爬虫框架、完整爬取代码等方面&a…...

97、Text2NeRF: Text-Driven 3D Scene Generation with Neural Radiance Fields

简介 论文地址 使用扩散模型来推断文本相关图像作为内容先验&#xff0c;并使用单目深度估计方法来提供几何先验&#xff0c;并引入了一种渐进的场景绘制和更新策略&#xff0c;保证不同视图之间纹理和几何的一致性 实现流程 简单而言&#xff1a; 文本-图片扩散模型生成一…...

【C++】多态(上) 多态 | 虚函数 | 重写 | final、override | 接口继承与实现继承 | 抽象类

一、多态 概念 多态&#xff0c;就是多种状态&#xff0c;即不同的对象去完成同一个行为时会产生出不同的状态。比如&#xff1a;买票时&#xff0c;成人要原价买&#xff0c;学生和老人就可以享受优惠价便宜一点儿。同样是买票这个行为&#xff0c;不同的对象来做就有不同的…...

国内怎么投资黄金,炒黄金有哪些好方法?

随着我国综合实力的不断强大&#xff0c;投资市场的发展也日臻完善&#xff0c;现已成为了国际黄金市场的重要组成部分&#xff0c;人们想要精准判断金市走向&#xff0c;就离不开对我国经济等信息的仔细分析。而想要有效提升盈利概率&#xff0c;人们还需要掌握国内黄金投资的…...

springboot实现数据脱敏

springboot实现数据脱敏 怎么说呢&#xff0c;写着写着发觉 ”这写的什么玩意“ 。 总的来说就是&#xff0c;这篇文章并不能解决数据脱敏问题&#xff0c;但以下链接可以。 SpringBoot中利用自定义注解优雅地实现隐私数据脱敏 然后回到本文&#xff0c;本来是想基于AOP代理&am…...

uniapp实现多时间段设置

功能说明&#xff1a; 1 点击新增时间&#xff0c;出现一个默认时间段模板&#xff0c;不能提交 2 点击“新增时间文本”&#xff0c;弹出弹窗&#xff0c;选择时间&#xff0c;不允许开始时间和结束时间同时为00:00&#xff0c; <view class"item_cont"> …...

uni-app - 去除隐藏页面右侧垂直滚动条

全局配置 "globalStyle": { //全局配置 "scrollIndicator":"none", // 不显示滚动条 "app-plus":{ "scrollIndicator":"none" // 在APP平台都不显示滚动条 } }局部配置 "path": "pages/ind…...

一次简单的 Http 请求异常处理 (请求的 url 太长, Nginx 直接返回 400, 导致请求服务异常)

1 结论 按照惯例直接说结论。 后台服务 A 有一个 Http 接口, 代码如下: RequestMapping(value "/user", method RequestMethod.GET) public List<UserInfoVo> getUserInfoByUserIds(RequestParam(value "userIds") List<String> userIds…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建

【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...