自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(62)
  • 收藏
  • 关注

转载 数据结构与算法-20-散列表(下)

我们已经学习了20节内容,你有没有发现,有两种数据结构,散列表和链表,经常会被放在一起使用。你还记得,前面的章节中都有哪些地方讲到散列表和链表的组合使用吗?我带你一起回忆一下。在链表那一节,我讲到如何用链表来实现LRU缓存淘汰算法,但是链表实现的LRU缓存淘汰算法的时间复杂度是O(n),当时我也提到了,通过散列表可以将这个时间复杂度降低到O(1)。在跳表那一节,我提到Redis的有序集合是使用跳表来实现的,跳表可以看作一种改进版的链表。当时我们也提到,Redis有序集合不仅使用了跳表,还用到了散列表。

2024-04-19 22:15:24 15

转载 数据结构与算法-19-散列表(中)

通过上一节的学习,我们知道,散列表的查询效率并不能笼统地说成是O(1)。它跟散列函数、装载因子、散列冲突等都有关系。如果散列函数设计得不好,或者装载因子过高,都可能导致散列冲突发生的概率升高,查询效率下降。在极端情况下,有些恶意的攻击者,还有可能通过精心构造的数据,使得所有的数据经过散列函数之后,都散列到同一个槽里。如果我们使用的是基于链表的冲突解决方法,那这个时候,散列表就会退化为链表,查询的时间复杂度就从O(1)急剧退化为O(n)。如果散列表中有10万个数据,退化后的散列表查询的效率就下降了10万倍。更

2024-04-18 22:42:43 26

转载 数据结构与算法-18-散列表(上)

Word 有拼写检查功能,如果输入错误的英文单词,就会用标红的方式提示“拼写错误”。你有没有想过,这个功能是如何实现的呢?这就涉及到本篇博客的内容——。

2024-04-18 22:38:38 24

转载 数据结构与算法-17-跳表

上两节我们讲了二分查找算法。当时我讲到,因为二分查找底层依赖的是数组随机访问的特性,所以只能用数组来实现。如果数据存储在链表中,就真的没法用二分查找算法了吗?实际上,我们只需要对链表稍加改造,就可以支持类似"二分"的查找算法。我们把改造之后的数据结构叫作跳表(Skip list),也就是今天要讲的内容。跳表这种数据结构对你来说,可能会比较陌生,因为一般的数据结构和算法书籍里都不怎么会讲。

2024-04-18 22:33:09 23

转载 数据结构与算法-16-二分查找(下)

前面的问题是查找第一个值等于给定值的元素,我现在把问题稍微改一下,查找最后一个值等于给定值的元素,又该如何做呢?如果你掌握了前面的写法,那这个问题你应该很轻松就能解决。你可以先试着实现一下,然后跟我写的对比一下。我们还是重点看第 11 行代码。如果 a[mid]这个元素已经是数组中的最后一个元素了,那它肯定是我们要找的;如果 a[mid]的后一个元素 a[mid+1]不等于 value,那也说明 a[mid]就是我们要找的最后一个值等于给定值的元素。

2024-04-18 22:26:19 3

转载 数据结构与算法-15-二分查找(上)

二分查找的底层需要依赖数组这种数据结构,二数组为了只持随机访问的特性,要求内存空间连续,对内存的要求比较苛刻。比如,我们有1GB大小的数据,如果希望用数组来存储,那就需要1GB的连续内存空间。注意这的的“连续”二字,也就是说,即便有2GB的内存空间剩余,但是如果这剩余的2GB内存空间都是零散的,没有连续的1GB大小的内存空间,那照样无法申请一个1GB大小的数组。而我们的二分查找是作用在数组这种数据结构之上的,所以太大的数据用数组存储就比较吃里了,也就不能用二分查找了。

2024-04-18 21:54:11 18

转载 数据结构与算法-14-排序优化

几乎所有的编程语言都会提供排序函数,比如C语言中qsort(),C++ STL中的sort()、stable_sort(),还有Java语言中的Collections.sort()。在今天的内容中,我分析了C语⾔的中的qsort()的底层排序算法,你能否分析一下你所熟悉的语言中的排序函数都是用什么排序算法实现的呢?前面我们讲到,快速排序比较适合来实现排序函数,但是,我们也知道,快速排序在最坏情况下的时间复杂度是O(n ),如何来解决这个“复杂度恶化”的问题呢?我们知道,快速排序是用递归来实现的。

2024-04-17 22:02:40 12

转载 数据结构与算法-13-线性排序

实际上,有时候要排序的数据并不都是等长的,比如我们排序牛津字典中的20万个英文单词,最短的只有1个字母,最长的我特意去查了下,有45个字目,中文翻译是尘肺病。不过,C[6]内存储的并不是考生,而是对应的考生个数。假设我们现在需要对D,a,F,B,c,A,z这个字符串进行排序,要求将其中所有小写字目都排在大写字母的前面,但小写字母内部和大写字母内部不要求有序。从图中可以看出,分数为3分的考生有3个,小于3分的考生有4个,所以,成绩为3分的考生在排序之后的有序数组R[8]中,会保存下标4,5,6的位置。

2024-04-17 22:00:58 8

转载 数据结构与算法-12-排序(下)

你可能会说,我有个很笨的办法,每次取数组中的最小值,将其移动到数组的最前面,然后在剩下的数组中继续找最小值,以此类推,执行K次,找到的数据不就是第K大元素了吗?根据分治、递归的处理思想,我们可以用递归排序下标从p到q-1之间的数据和下标从q+1到r之间的数据,直到区间缩小为1,就说明所有的数据都有序了。从我们的原理分析和伪代码可以看出,归并排序的执行效率与要排序的原始数组的有序程度有关,所以其时间复杂度是非常稳定的,不管是最好情况、最坏情况,不过,时间复杂度就并不是O(n)了,而是O(K * n)。

2024-04-17 21:57:17 4

转载 数据结构与算法-11-排序(上)

(默认从小到大为有序),我想你应该已经想到了。关于逆序度,我就不举例子讲了。你可以对照我讲的有序度的例子自己看下。1。

2024-04-17 21:53:48 9

转载 数据结构与算法-10-递归

从图中,我们可以直观地看到,想要计算f(5),需要先计算f(4)和f(3),而计算f(4)还需要计算f(3),因此,f(3)就被计算了很多次,还是电影院的例子,第一排的就不需要再继续询问任何人,就知道自己在哪一排,也就是f(1)=1,这就是递归的终止条件。2,2,2,1这样子上去,也可以1,2,1,1,2这样子上去,总之之法有很多,那如何用编程求得总共有多少种走法呢?如果有7个台阶,你可以。比如电影院那个例子,你求解“自己在哪一排”的思路,和前面一排人求解“自己在哪一排”的思路,是一模一样的。

2024-04-17 21:52:05 5

转载 数据结构与算法-09-队列

循环队列,顾名思义,它长得像一个环。原本数组是有头有尾的,是一条直线。现在我们把首尾相连,扳成了一个环。我画了一张图,你可以直观地感受一下。图中这个队列的大小为8,当前head=4,tail=7。当有一个新的元素a入队时,我们放入下标为7的位置。但这个时候,我们并不把tail更新为8,而是将其在环中后移一位,到下标为0的位置。当再有一个元素b入队时,我们将b放入下标为0的位置,然后tail加1更新为1。阻塞队列其实就是在队列基础上增加了阻塞操作。

2024-04-17 21:50:30 6

转载 数据结构与算法-08-栈

不管是顺序栈还是链式栈,我们存储数据只需要一个大小为n的数组就够了。在入栈和出栈过程中,只需要一两个临时变量存储空间,

2024-04-17 21:48:38 16

转载 数据结构与算法-07-链表(下)

跟插入类似123ifnull) {null;从前面的一步一步分析,我们可以看出,针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理。这样代码实现起来就会很繁琐,不简洁,而且也容易因为考虑不全而出错。如何来解决这个问题呢?我画了一个带头链表,你可以发现,哨兵结点是不存储数据的。因为哨兵结点一直存在,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为相同的代码实现逻辑了。

2024-04-17 21:46:27 8

转载 数据结构与算法-06-链表(上)

今天我们来聊聊“链表(Linked list)”这个数据结构。学习链表有什么用呢?为了回答这个问题,我们先来讨论一个经典的链表应⽤场景,那就是LRU缓存淘汰算法。缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,比如常用的CPU缓存、数据库缓存、浏览器缓存等等。缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的策略有三种:这些策略你不用死记,我打个比方你很容易就明白了。假如说,你买了很多本技术书,但有⼀天你发现,这些书太

2024-04-17 21:40:10 7

转载 数据结构与算法-05-数组

这个处理思想在快排中也会用到,在排序那一节具体来讲,这里就说到这儿。例子:假设数组 a[10] 中存储了如下 5 个元素 : a, b, c, d, e.现在需要将元素 x 插入到第 3 个位置.只需要将 c 放入到 a[5],将 a[2] 赋值为 x 即可.

2024-04-17 21:16:17 250

转载 数据结构与算法-04-复杂度分析(下)

另外,要查找的数据出现在 0~n-1 这n个位置的概率与时一样的,为1/n。难点,平均时间复杂度 就是他有可能是第1.2.3...n,中的某个分钟回来,那平均就是1+2+3+...n/n,把所有可能出现的情况的时间复杂度相加除以情况数。那么也有可能遇到突发情况,比如说电梯人多吖,路上摔了一胶,天知道他去干了什么,用了n分钟,没办法👐,主上有令,n分钟限时,那这就是“最坏的情况”。因此,前面的推导过程中存在的最大问题就是,没有将各种情况发生的概率考虑进去,如果我们把每种情况发生的概率也考虑进来。

2024-04-17 21:11:05 441

转载 数据结构与算法-03-复杂度分析(上)

当然,渐进式时间,空间复杂度分析只是一个理论模型,只能提供给粗略的估计分析,我们不能直接断定就觉得O(logN)的算法一定优于O(n), 针对不同的宿主环境,不同的数据集,不同的数据量的大小,在实际应用上面可能真正的性能会不同,个人觉得,针对不同的实际情况,进而进行一定的性能基准测试是很有必要的,比如在统一一批手机上(同样的硬件,系统等等)进行横向基准测试,进而选择适合特定应用场景下的最有算法。O(1)、O(n)、O(n2 ),像 像 O(logn)、O(nlogn) 这样的对数阶复杂度平时用不到。

2024-04-17 20:52:56 417

原创 数据结构与算法-概要说明

​ 20 个 最常用的、最基础的数据结构与算法,不管是应付面试还 是工作需要,只要集中精力逐一攻克,就足够了。

2024-04-17 20:50:56 343

原创 类加载器、双亲委派、SPI机制

JVM 并不是在启动时就把所有的​​.class​​​文件都加载一遍,而是程序在运行过程中用到了这个类才去加载。除了启动类加载器外,其他所有类加载器都需要继承抽象类​​ClassLoader​​,这个抽象类中定义了三个关键方法,理解清楚它们的作用和关系非常重要。//每个类加载器都有个父加载器//查找一下这个类是不是已经加载过了Class<?//如果没有加载过//先委派给父加载器去加载,注意这是个递归调用if (parent!

2024-04-15 23:29:39 621

原创 设计模式-33-常用的设计思想、原则和模式

主要包括面向对象、设计原则、编码规范、重构技巧、设计模式这五个部分。

2024-04-14 23:07:03 612

原创 设计模式-32-六大原则

主要包括面向对象、设计原则、编码规范、重构技巧、设计模式这五个部分。

2024-04-14 23:02:56 225

原创 设计模式-31-总结回顾23种经典设计模式的原理、背后的思想、应用场景等

23种经典设计模式共分为3种类型,分别是创建型、结构型和行为型。现在,我们把这3种类型分成3个对应的小模块,逐一带你回顾一下每一种设计模式的原理、实现、设计意图和应用场景。话不多说,让我们正式开始今天的复习吧!

2024-04-14 18:18:39 870

原创 设计模式-30-中介模式-行为型模式

现在,我们来学习23种经典设计模式中的最后一个,中介模式。跟前面刚刚讲过的命令模式、解释器模式类似,中介模式也属于不怎么常用的模式,应用场景比较特殊、有限,但是,跟它俩不同的是,中介模式理解起来并不难,代码实现也非常简单,学习难度要小很多。如果你对中介模式有所了解,你可能会知道,中介模式跟之前讲过的观察者模式有点相似,所以,今天我们还会详细讨论下这两种模式的区别。

2024-04-14 18:15:08 692

原创 设计模式-29-解释器模式-行为型模式

上一节,我们学习了命令模式。命令模式将请求封装成对象,方便作为函数参数传递和赋值给变量。它主要的应用场景是给命令的执行附加功能,换句话说,就是控制命令的执行,比如,排队、异步、延迟执行命令、给命令执行记录日志、撤销重做命令等等。总体上来讲,命令模式的应用范围并不广。现在,我们来学习解释器模式,它用来描述如何构建一个简单的“语言”解释器。比起命令模式,解释器模式更加小众,只在一些特定的领域会被用到,比如编译器、规则引擎、正则表达式。所以,解释器模式也不是我们学习的重点,你稍微了解一下就可以了。

2024-04-14 17:54:39 513

原创 设计模式-28-命令模式-行为型模式

如何利用命令模式实现一个手游后端架构

2024-04-14 17:23:05 716

原创 设计模式-27-备忘录模式-行为型模式

上两节,我们学习了访问者模式。在23种设计模式中,访问者模式的原理和实现可以说是最难理解的了,特别是它的代码实现。其中,用Single Dispatch来模拟Double Dispatch的实现思路尤其不好理解。不知道你有没有将它拿下呢?如果还没有弄得很清楚,那就要多看几遍、多自己动脑经琢磨一下。现在,我们学习另外一种行为型模式,备忘录模式。这个模式理解、掌握起来不难,代码实现比较灵活,应用场景也比较明确和有限,主要是用来防丢失、撤销、恢复等。所以,相对于上两节课,今天的内容学起来相对会比较轻松些。

2024-04-14 16:44:11 816

原创 设计模式-26-访问者模式(下)-行为型模式

上一节中,我们学习了访问者模式的原理和实现,并且还原了访问者模式诞生的思维过程。总体上来讲,这个模式的代码实现比较难,所以应用场景并不多。从应用开发的角度来说,它的确不是我们学习的重点。话不多说,让我们正式开始今天的学习吧!

2024-04-14 16:12:19 531

原创 设计模式-25-访问者模式(上)-行为型模式

前面我们讲到,大部分设计模式的原理和实现都很简单,不过也有例外,比如今天要讲的访问者模式。它可以算是23种经典设计模式中最难理解的几个之一。因为它难理解、难实现,应用它会导致代码的可读性、可维护性变差,所以,访问者模式在实际的软件开发中很少被用到,在没有特别必要的情况下,建议你不要使用访问者模式。尽管如此,为了让你以后读到应用了访问者模式的代码的时候,能一眼就能看出代码的设计意图,同时为了整个专栏内容的完整性,我觉得还是有必要给你讲一讲这个模式。

2024-04-14 16:11:03 794

原创 设计模式-24-迭代器模式(下)-行为型模式

上两节,我们学习了迭代器模式的原理、实现,并且分析了在遍历集合的同时增删集合元素,产生不可预期结果的原因以及应对策略。现在,我们再来看这样一个问题:如何实现一个支持“快照”功能的迭代器?这个问题算是对上一节课内容的延伸思考,为的是帮你加深对迭代器模式的理解,也是对你分析、解决问题的一种锻炼。你可以把它当作一个面试题或者练习题,在看我的讲解之前,先试一试自己能否顺利回答上来。

2024-04-14 16:03:19 813

原创 设计模式-23-迭代器模式(中)-行为型模式

上一节中,我们通过给ArrayList、LinkedList容器实现迭代器,学习了迭代器模式的原理、实现和设计意图。迭代器模式主要作用是解耦容器代码和遍历代码,这也印证了我们前面多次讲过的应用设计模式的主要目的是解耦。上一节中讲解的内容都比较基础,现在,我们来深挖一下,如果在使用迭代器遍历集合的同时增加、删除集合中的元素,会发生什么情况?应该如何应对?如何在遍历的同时安全地删除集合元素?

2024-04-14 16:00:28 993

原创 设计模式-22-迭代器模式(上)-行为型模式

上一节,我们学习了状态模式。状态模式是状态机的一种实现方法。它通过将事件触发的状态转移和动作执行,拆分到不同的状态类中,以此来避免状态机类中的分支判断逻辑,应对状态机类代码的复杂性。现在,我们学习另外一种行为型设计模式,迭代器模式。它用来遍历集合对象。不过,很多编程语言都将迭代器作为一个基础的类库,直接提供出来了。在平时开发中,特别是业务开发,我们直接使用即可,很少会自己去实现一个迭代器。不过,知其然知其所以然,弄懂原理能帮助我们更好的使用这些工具类,所以,我觉得还是有必要学习一下这个模式。

2024-04-14 15:58:25 717

原创 设计模式-21-状态模式-行为型模式

有限状态机,英文翻译是Finite State Machine,缩写为FSM,简称为状态机。状态机有3个组成部分:状态(State)、事件(Event)、动作(Action)。其中,事件也称为转移条件(Transition Condition)。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。对于刚刚给出的状态机的定义,我结合一个具体的例子,来进一步解释一下。“超级马里奥”游戏不知道你玩过没有?

2024-04-13 22:37:27 526

原创 设计模式-20-职责链模式(下)-行为型模式

上一节,我们学习职责链模式的原理与实现,并且通过一个敏感词过滤框架的例子,展示了职责链模式的设计意图。本质上来说,它跟大部分设计模式一样,都是为了解耦代码,应对代码的复杂性,让代码满足开闭原则,提高代码的可扩展性。除此之外,我们还提到,职责链模式常用在框架的开发中,为框架提供扩展点,让框架的使用者在不修改框架源码的情况下,基于扩展点添加新的功能。实际上,更具体点来说,职责链模式最常用来开发框架的过滤器和拦截器。

2024-04-13 21:53:13 859

原创 设计模式-19-职责链模式(上)-行为型模式

前几节中,我们学习了模板模式、策略模式,今天,我们来学习职责链模式。这三种模式具有相同的作用:复用和扩展,在实际的项目开发中比较常用,特别是框架开发中,我们可以利用它们来提供框架的扩展点,能够让框架的使用者在不修改框架源码的情况下,基于扩展点定制化框架的功能。今天,我们主要讲解职责链模式的原理和实现。除此之外,我还会利用职责链模式,带你实现一个可以灵活扩展算法的敏感词过滤框架。

2024-04-13 20:49:19 932

原创 设计模式-18-策略模式(下)-行为型模式

上一节,我们主要介绍了策略模式的原理和实现,以及如何利用策略模式来移除if-else或者switch-case分支判断逻辑。今天,我们结合“给文件排序”这样一个具体的例子,来详细讲一讲策略模式的设计意图和应用场景。除此之外,在今天的讲解中,我还会通过一步一步地分析、重构,给你展示一个设计模式是如何“创造”出来的。通过今天的学习,你会发现,。

2024-04-13 19:53:28 1018

原创 设计模式-17-策略模式(上)-行为型模式

策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。因为所有的策略类都实现相同的接口,所以,客户端代码基于接口而非实现编程,可以灵活地替换不同的策略。@Override//具体的算法...@Override//具体的算法...

2024-04-13 17:11:16 843

原创 设计模式-16-模板模式(下)-回调-行为型模式

上一节中,我们学习了模板模式的原理、实现和应用。它常用在框架开发中,通过提供功能扩展点,让框架用户在不修改框架源码的情况下,基于扩展点定制化框架的功能。除此之外,模板模式还可以起到代码复用的作用。复用和扩展是模板模式的两大作用,实际上,还有另外一个技术概念,也能起到跟模板模式相同的作用,那就是(Callback)。今天我们今天就来看一下,回调的原理、实现和应用,以及它跟模板模式的区别和联系。

2024-04-13 16:35:43 1017

原创 设计模式-15-模板模式(上)-行为型模式

在Java AbstractList类中,addAll()函数可以看作模板方法,add()是子类需要重写的方法,尽管没有声明为abstract的,但函数实现直接抛出了UnsupportedOperationException异常。前提是,如果子类不重写是不能使用的。

2024-04-13 16:33:47 952

原创 设计模式-14-观察者模式(下)-行为型模式

EventBus翻译为“事件总线”,它提供了实现观察者模式的骨架代码。我们可以基于此框架,非常容易地在自己的业务场景中实现观察者模式,不需要从零开始开发。其中,Google Guava EventBus就是一个比较著名的EventBus框架,它不仅仅支持异步非阻塞模式,同时也支持同步阻塞模式现在,我们就通过例子来看一下,Guava EventBus具有哪些功能。// 依赖注入// 同步阻塞模式// 异步非阻塞模式//省略输入参数的校验代码。

2024-04-13 16:30:13 591

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除