![](https://img-blog.csdnimg.cn/20201014180756922.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
数据结构与算法
文章平均质量分 71
von Libniz
这个作者很懒,什么都没留下…
展开
-
KMP算法多图详解(python)
目录1.1 串1.2 Brute Force1.3 KMP算法1.4 next数组构造1.1 串KMP算法主要是用来解决串的匹配问题,为此,在学习KMP之前我们先来了解一下串的相关知识。串是由若干个字符组成的有限序列,也就是我们熟悉的字符串。此外,字符串中还有前后缀,真前后缀的概念,对于字符串ABCD,它的前后缀如下表所示。显然真前后缀与前后缀的区别在于不能包含字符串自身。1.2 Brute Force字符串的匹配问题,也就是查找一个字符串(称为模式串pattern)在另一个字符串(称为文本原创 2021-08-04 09:00:08 · 2143 阅读 · 3 评论 -
python实现跳表SkipList
跳表跳表,又叫做跳跃表、跳跃列表,在有序链表的基础上增加了“跳跃”的功能,由William Pugh于1990年发布,设计的初衷是为了取代平衡树(比如红黑树)。Redis、LevelDB 都是著名的 Key-Value 数据库,而Redis中 的 SortedSet、LevelDB 中的 MemTable 都用到了跳表。对比平衡树,跳表的实现和维护会更加简单,跳表的搜索、删除、添加的平均时间复杂度是 O(logn)。跳表的结构如图所示:可以发现,对于一个节点Node,其含有多个next指针,不同原创 2021-07-24 22:39:45 · 1254 阅读 · 5 评论 -
python实现布隆过滤器
布隆过滤器Bloom Filter布隆过滤器于1970年由布隆提出,它是一个空间效率高的概率型数据结构,可以用来告诉你:一个元素一定不存在或者可能存在。优点:空间效率和查询时间都远远超过一般的算法(使用哈希表将占用大量的内存空间)。缺点:有一定的误判率、删除困难。它实质上是一个很长的二进制向量和一系列随机映射函数(Hash函数)组成,常用于网页黑名单系统、垃圾邮件过滤系统、爬虫的网址判重系统、解决缓存穿透问题。原理布隆过滤器中,二进制数组的长度和哈希函数的个数由问题的数量规模、要求的错误率决定。原创 2021-07-20 23:44:14 · 2479 阅读 · 5 评论 -
基于rank、path halving实现并查集(python)
目录1 并查集1.1 常用优化方法1.2 代码实现1 并查集并查集也叫作不相交集合(Disjoint Set),可以高效实现元素间集合关系的查找与合并。查找(Find):查找元素所在的集合(这里的集合并不是特指Set这种数据结构,是指广义的数据集合)。合并(Union):将两个元素所在的集合合并为一个集合。例如上图中的0、1、2、3属于同一个集合,4、6一个集合,以及7、8、9也组成了一个集合。1.1 常用优化方法Size:在union合并两个子集时,通过size(树中元素的数量)原创 2021-07-03 16:05:58 · 262 阅读 · 1 评论 -
拓扑排序的实现(python)
目录1 AOV网(Activity On Vertex Network)2 拓扑排序(Topological Sort)2.1 实现思路2.2 代码实现1 AOV网(Activity On Vertex Network)一项大的工程常被分为多个小的子工程,子工程之间可能存在一定的先后顺序,即某些子工程必须在其他的一些子工程完成后才能开始。在现代化管理中,人们常用有向图来描述和分析一项工程的计划和实施过程,子工程被称为活动(Activity)。以顶点表示活动、有向边表示活动之间的先后关系,这样原创 2021-07-02 16:17:04 · 2462 阅读 · 0 评论 -
图的非递归深搜遍历图示与代码(python)
1 非递归深搜图的深搜递归形式非常简单,相信大家都会,在此就来实现一下非递归版本的深搜。1.1 图示对于以上的图,从1开始的深搜遍历结果应该是1 2 3 4 0。那么如何通过非递归形式得到该结果呢,下面以该图为例进行演示:首先我们准备要一个栈来模拟递归的过程,以及一个集合来存储已经访问的节点,以免因为图中的环结构让子节点再次进入父节点访问。我们首先处理节点1,这里将入栈操作视为对节点的访问,于是我们将节点1入栈并访问,再加入已访问集合。从这开始,我们就要对栈中的元素进行不断地原创 2021-07-01 22:29:40 · 822 阅读 · 1 评论 -
使用邻接表实现数据结构图(python)
目录1 图1.1 有向图1.2 完全图1.3 稀疏、稠密图1.4 连通图1.5 图的实现方案1.5.1 邻接矩阵(Adjacency Matrix)1.5.2 邻接表(Adjacency List)1.6 代码实现1.6.1 图的基础接口1.6.2 顶点定义1.6.3 边的定义1.6.4 整体代码1 图图由顶点(vertex)和边(edge)组成,通常表示为 G = (V, E)。G表示一个图,V是顶点集,E是边集,顶点集V有穷且非空,任意两个顶点之间都可以用边来表示它们之间的关原创 2021-06-29 22:11:01 · 5581 阅读 · 3 评论 -
排序算法(五)快速排序的实现(python)
目录1 快速排序1.1 执行流程1.2 轴点构造1.3 代码实现1.4 时间复杂度1 快速排序1960年由查尔斯·安东尼·理查德·霍尔(Charles Antony Richard Hoare,缩写为C. A. R. Hoare)提出。简称为东尼·霍尔(Tony Hoare)。1.1 执行流程(1)从序列中选择一个轴点元素(pivot)。我们先假设每次选择 0 位置的元素为轴点元素。(2)利用 pivot 将序列分割成 2 个子序列。将小于 pivot 的元素放在pivot前面原创 2021-06-23 15:46:10 · 263 阅读 · 1 评论 -
排序算法(四)归并排序的实现(python)
1 归并排序1945年由约翰·冯·诺伊曼(John von Neumann)首次提出。1.1 执行流程(1) 不断地将当前序列平均分割成2个子序列,直到不能再分割(序列中只剩1个元素)。(2)不断地将2个子序列合并成一个有序序列,直到最终只剩下1个有序序列。1.2 分割divide的实现归并排序的思想是,如果要对一个数组进行排序,则先将数组分割为两部分,将两部分数组排序完后,将他们合并在一起成为一个新的有序数组,而对分割形成的数组,依旧采用这个思路进行排序,显然这是一个递归的过程。而归并原创 2021-06-11 23:41:44 · 423 阅读 · 3 评论 -
排序算法(三)插入排序与其优化(python)
目录1 插入排序1.1 执行流程1.2 逆序对1.3 代码实现1.4 优化11.5 优化21.5.1 普通二分查找1.5.2 二分查找插入位置1.5.3 使用二分的插入排序1.6 时间空间复杂度1 插入排序插入排序与我们平时打扑克牌非常相似,将新摸到的牌插入到已有的牌中合适的位置,而已有的牌往往是有序的。1.1 执行流程(1)在执行过程中,插入排序会将序列分为2部分,头部是已经排好序的,尾部是待排序的。(2)从头开始扫描每一个元素,每当扫描到一个元素,就将它插入到头部合原创 2021-06-09 16:03:55 · 505 阅读 · 2 评论 -
排序算法(二)选择排序与其改进之堆排序(python)
目录1 选择排序1.1 执行流程2 堆排序2.1 执行流程2.2 批量建堆2.2.1 找到最后一个非叶子节点2.2.2 代码实现2.3 下滤操作2.4 完成堆排序2.5 测试性能与准确性2.6 时间空间复杂度1 选择排序1.1 执行流程选择排序和冒泡排序非常相似,都是每次找出未排序元素中最大的那个,将其放置到末尾(升序),然后进行n-1一次。但和冒泡排序不同的是,他不需要进行大量的交换操作,只需先找到最大的元素,然后与末尾元素交换位置即可。代码思路也很简单,通过整形变量记录最原创 2021-06-07 00:07:26 · 528 阅读 · 4 评论 -
排序算法(一)冒泡排序与两种改进(python)
目录1 冒泡排序1.1 执行流程1.2 代码思路1.3 测试正确性1.4 冒泡排序改进11.4 冒泡排序改进21.4.1 代码实现细节1.5 三种排序的比较1.6 时间空间复杂度1 冒泡排序1.1 执行流程冒泡排序也叫做起泡排序。以升序为例,其执行流程为:从头开始比较每一对相邻元素,如果第1个比第2个大,就交换它们的位置,执行完一轮之后(遍历完0到n-1的元素),最后一个元素就是整个数组中最大的元素;之后再对0至n-2的元素进行相同操作…直到对最后两个元素作比较(索引为0与1的元原创 2021-06-04 23:10:25 · 600 阅读 · 2 评论 -
哈夫曼编码(Huffman Coding)多图详细解析
哈夫曼编码哈夫曼编码,又称为霍夫曼编码,它是现代压缩算法的基础。假如我们需要将字符串ABBBCCCCCCCCDDDDDDEE通过二进制编码进行传输,那应该怎么将字符转换为二进制码?方法一:转换为ASCII码直接将字母转换为对应的ASCII码数字,再将ASCII码转换为对应的二进制码字母ASCII码二进制码A65100 0001B66100 0010C67100 0011D68100 0100E69100 0101显然这样的方式使原创 2021-04-17 11:52:40 · 32309 阅读 · 6 评论 -
优先级队列的实现
优先级队列(Priority Queue)优先级队列本身也是一个队列,只不过常规的队列是先进先出,而优先级队列可以插队,即让队列中优先级最高的元素先出队。在Java中,普通队列是由双向链表来实现的,如果优先级队列也使用双向链表,那么通过遍历找优先级最高的节点需要O(n)的时间复杂度,但如果使用二叉堆,那么入队和出队的时间复杂度都可以降到O(logn)。接口方法功能int size()获取队列大小void isEmpty()是否为空void clear()清空原创 2021-04-16 16:19:44 · 490 阅读 · 1 评论 -
堆的概念与二叉堆的实现详细图解
文章目录1.0 堆的概念1.1堆的使用1.2 堆的结构1.3堆的基本接口2.0 二叉堆2.1 二叉堆的结构2.2 二叉堆的具体实现2.2.1 创建二叉堆2.2.1 get方法2.2.2 add方法2.2.3 remove方法2.2.4 replace方法2.2.5 整体代码3.0 top-k问题1.0 堆的概念1.1堆的使用假如我们需要设计一种数据接口,来实现一组数据的存储,并实现三个功能:获取最大元素删除最大元素添加元素我们可以采用什么数据结构?数据结构获取最大值删除最大值原创 2021-04-14 15:59:58 · 377 阅读 · 1 评论 -
映射TreeMap的实现
映射MapMap 在有些编程语言中也叫做字典Map接口public interface Map<K,V> { /** * 存入键值对 * @param key * @param value * @return 返回之前key对应的value */ V put(K key,V value); V get(K key); V remove(K key); int size(); boolean原创 2021-04-07 10:18:54 · 214 阅读 · 0 评论 -
Set集合的简单实现
Set集合Set集合的特点是不存放重复的元素,常用于去重,而它的具体实现可以用到其他的一些数据结构如:动态数组、链表、二叉搜索树。/** * Set集合与其抽象方法 * @param <E> */public interface Set<E> { int size(); boolean isEmpty(); boolean contains(E element); void clear(); void add(E element);原创 2021-04-06 12:39:02 · 720 阅读 · 0 评论 -
红黑树(一)插入节点后的平衡操作超详细图解
一、红黑树的概念1.1简介红黑树和AVL树一样,也是一种自平衡的二叉搜索树,只不过它的平衡并不像AVL树那么严格(AVL树要求任意节点左右子树的高度差不超过1),所以在树的高度上会比AVL树高一些,搜索的速度也会慢一些,但它插入与删除只需要进行O(1)级别的旋转操作,而AVL树的删除需要进行O(logn)的旋转,因此综合搜索、插入、删除这几个方面,红黑树的性能会优于AVL树,也正是如此,在现实生活中红黑树的应用也更多。1.2红黑树的五条原则红黑树具有五条原则,只要满足了这五条原则,一棵二叉搜索树就是原创 2021-03-26 10:14:33 · 1005 阅读 · 1 评论 -
请问,你心里有B树吗??(B树添加、删除操作详细图解)
1 B树B树(B-树)是一种平衡的多路搜索树,多用于文件系统、数据库的实现。1.1 B树的特点1 个节点可以存储超过 2 个元素、可以拥有超过 2 个子节点拥有二叉搜索树的一些性质平衡,每个节点的所有子树高度一致比较矮1.2 m阶B树m阶B树中的m指一棵B树中存在的最大子节点数,如下图中的就是一棵3阶B树,也可以称为2-3树(2-3是非根节点可以有的子节点数,见下性质)1.2.1 m阶B树的性质假设B树一个节点可以存储元素的个数为:x,则:根节点中:1<=x<m原创 2021-03-17 17:07:37 · 624 阅读 · 1 评论 -
AVL添加节点后的平衡操作(二)代码实现
平衡思路在上篇博客(https://blog.csdn.net/Demon_LMMan/article/details/114835679)中已经详细描述了添加节点的平衡思路,下面进行代码实现。代码实现public class AVL<E> extends BST<E>{...}显然AVL树应该继承自BST,但由于平衡操作和高度息息相关,所以需要在节点中增加高度属性,并增加一些适当的方法。此外由于BST于AVL所使用的节点不同,我们应新建一个方法用于创建新节点,并在A原创 2021-03-16 15:04:51 · 124 阅读 · 0 评论 -
AVL树添加节点后的平衡操作(一)逻辑分析:左旋、右旋、双旋(超详细图解)
AVL树AVL树是最早发明的自平衡二叉搜索树之一,其名字来源于两位发明它的科学家G. M. Adelson-Velsky 和 E. M. Landis(来自苏联的科学家)。AVL树的特点AVL树中的每个节点都有一个平衡因子(Balance Factor),即左右子树高度之差。AVL树的特点如下:每个节点的平衡因子只可能是 1、0、-1(绝对值 ≤ 1,如果超过 1,称之为“失衡“)每个节点的左右子树高度差不超过1搜索、添加、删除的时间复杂度是O(logn)在AVL树中添加节点假如我们对下原创 2021-03-15 19:11:30 · 2751 阅读 · 0 评论 -
平衡二叉搜索树的概念与应用
使用平衡二叉搜索树的原因之前讲过,如果要从n个数种搜索一个数,使用动态数组的时间复杂度是O(n),而在动态数组中插入、删除元素的时间复杂度也是O(n),即使维护一个有序的动态数组,使用二分查找,搜索的时间复杂度是O(logn),插入和删除的时间复杂度仍然是O(n)。而使用二叉搜索树,则可以将插入、删除、搜索的复杂度都降到O(logn)。但是,不是所有的情况都可以做到O(logn)的复杂度,比如下面这棵二叉搜索树,搜索的时间复杂度就是O(n)了。而对于下面这棵树,则仍保持O(logn)的效率。而当n原创 2021-03-11 21:37:45 · 259 阅读 · 0 评论 -
BST与AVL打印结果对比
打印对比结果手撸了一波AVL,结果各种bug…。Debug2小时,终于是好了,打印结果看看。bst:1-p(null)─┐ │ 2-p(1)─┐ │ 3-p(2)─┐ │ 4-p(3)─┐ │ 5-p(4)─┐原创 2021-03-08 17:39:13 · 207 阅读 · 0 评论 -
二叉树中的迭代(非递归)遍历方法:先序+中序+后续+层序
先序遍历迭代方法先序遍历迭代方法,通过访问父节点后将父节点存入栈中,访问左子树,在访问完左子树后,再从栈中取出父节点并指向右子树访问来实现。具体见下代码。public void preorderTraveral(TreeNode<E> root){ if(null==root)return; Stack<TreeNode<E>> stack=new Stack<>();//定义栈 TreeNode<E> p=roo原创 2021-03-05 16:23:10 · 181 阅读 · 0 评论 -
表达式树的概念
表达式树表达式树是指:一颗树中,所有叶子节点的元素值都是数字,而所有父节点(即有子节点的节点)的元素值都是运算符。如下图中的树。对该树分别进行先序、中序、后续遍历,则可以得到对应的三种表达式前序遍历:+ - / 8 9 5 * * 10 11 7中序遍历:8 / 9 - 5 + 10 * 11 * 7后续遍历:8 9 / 5 - 10 11 * 7 * +四则运算的表达式四则运算的表达式可以分为3种前缀表达式(prefix expression),又称波兰表达式,符号出现在数字前中原创 2021-03-05 14:34:48 · 1273 阅读 · 0 评论 -
二叉搜索树(二)实现删除操作
删除的所有情况删除的是叶子节点(1)该叶子节点不是根节点此时将父节点的left或right修改为null即可。(2)是根节点将根节点root修改为null即可。删除的是度为1的节点(1)该节点不是根节点既然要删掉13,势必要找到代替的节点与10相连,这里使用后继节点12(即中序遍历中13之后的节点,也可以用前驱节点,具体见二叉树中前驱节点与后继节点的查找),就可以不破坏二叉树中原有的顺序。删除后:(2)是根节点此时直接将根节点指向其子节点即可。删除的是度为2的节点依旧原创 2021-02-27 14:45:58 · 348 阅读 · 0 评论 -
二叉树中前驱节点与后继节点的查找
前驱节点基本概念一个节点的前驱节点,也就是中序遍历中该节点的前一个节点。举个例子:这棵树的中序遍历顺序为:1,2,4,5,7,8,9,11(因为是二叉搜索树,所以中序遍历的顺序是递增)。那么节点7的前驱是5,节点8的前驱是7。查找思路那么该如何查找一个节点的前驱呢,可以确定的是,中序遍历是先访问左子树中的所有节点,再访问根节点,那么由访问次序可知,前驱节点是左子树中最后一个被访问的节点,当然,前提是有左子树。那么又该如何找到左子树中最后访问的节点?显而易见,由中序遍历的次序是左中右可知,最后访原创 2021-02-26 14:48:03 · 2812 阅读 · 0 评论 -
获取树高度的两种方法与完全二叉树的判断
树的高度树的高度是节点高度的最大值。每一层节点的高度如图所示。方法一:递归根节点的高度显然就是二叉树的高度。/*** 获取树的高度 * @return */public int height(){ return height2(root);}/** * 使用递归方法获取树的高度 * @param node * @return */private int height1(Node<E> node){ if(node==null)return 0;原创 2021-02-24 22:46:35 · 1378 阅读 · 0 评论 -
树的四种遍历方法与增强遍历功能
先序遍历先序遍历先访问根节点,再访问左子树,最后访问右子树。private void preOrderTraversal(Node<E> node){ if(null==node)return //访问当前节点 System.out.print(node.element+" "); //访问左子树 preOrderTraversal(node.left); //访问右子树 preOrderTraversal(node.right);}//提供给外界使用public voi原创 2021-02-24 22:07:30 · 340 阅读 · 0 评论 -
二叉搜索树(一)二叉搜索树的概念与实现add方法、简单打印
二叉搜索树的优势如果要在n个整数中搜索某个整数,查看其是否存在。(1)假如使用动态存放元素,从第0个位置开始遍历搜索,平均时间复杂度为O(n)。(2)假如维护一个有序的动态数组,使用二分查找,最坏时间复杂度是O(lngn)。但是添加和删除的时间复杂度仍然是O(n)。(3)使用二叉搜索树,添加、删除、搜索的最坏时间复杂度均可优化至O(logn)。二叉搜索树的概念(一)二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为 BST。二叉搜索树又被称为二叉查找树、二叉排序树。任意一个原创 2021-02-24 17:49:11 · 240 阅读 · 0 评论 -
二叉树中的基本概念与公式推导
树的结构树(tree)的基本概念节点、根节点、父节点、子节点、兄弟节点一棵树可以没有任何节点,称为空树一棵树可以只有 1 个节点,也就是只有根节点子树、左子树、右子树节点的度(degree):节点所含有子树的个数树的度:所有节点度中的最大值叶子节点(leaf):度为 0 的节点非叶子节点:度不为 0 的节点层数(level):根节点在第 1 层,根节点的子节点在第 2 层,以此类推(有些教程也从第 0 层开始计算)节点的深度(depth):从原创 2021-02-21 11:16:00 · 3214 阅读 · 0 评论 -
吹牛必备之循环队列与双端循环队列
循环队列循环队列概念所谓的循环队列,其实还是一个队列,从外部调用的接口和实际的功能上看和普通的队列完全没有区别,而之所以它叫循环队列而不是队列,是因为它用于实现的底层逻辑和一般队列不同,一般队列底层采用双向链表,而循环队列底层采用索引不断循环的数组实现。循环队列操作图示建立循环队列,势必要先建立一个数组:E elements=(E[]) new Object[8];//其中E表示泛型假如向队列中入队11,22,33,44这四个元素,则其存储结构如图所示。此时若将元素55入队列,则直接在尾元原创 2021-02-19 23:08:25 · 989 阅读 · 0 评论 -
队列与双端队列的实现
队列队列是一种特殊的线性表,只能在头尾两端进行操作队尾(rear):只能从队尾添加元素,一般叫做 enQueue,入队队头(front):只能从队头移除元素,一般叫做 deQueue,出队先进先出的原则,First In First Out,FIFOjava官网的Queue是一个接口,具体实现是双向链表LinkedList示意图队列的接口设计方法声明功能介绍int size()元素的数量boolean isEmpty()是否为空void clear(原创 2021-02-19 16:59:09 · 466 阅读 · 1 评论 -
用Java语言实现栈
栈的定义栈是一种特殊的线性表,只能在一端进行操作。往栈中添加元素的操作,一般叫做 push,入栈。从栈中移除元素的操作,一般叫做 pop,出栈(只能移除栈顶元素,也叫做:弹出栈顶元素)。后进先出的原则,Last In First Out,LIFO。栈的结构栈的底层其实就是线性表,可以用链表或者动态数组实现,且由其在线性表尾部频繁增加删除元素的特点,使用链表与动态数组的性能差不多。出栈:入栈:链表的接口设计方法声明功能介绍int size()返回链表大小原创 2021-02-19 14:31:59 · 365 阅读 · 0 评论 -
链表(三)单向循环链表与双向循环链表
单向循环链表观察单向链表和单向循环链表的区别:显然,循环链表和普通链表的区别在于,尾节点的next指针指向了头节点。而对于这条指向的维护也只在于add和remove方法,因此循环链表仅仅在这个方法上有所不同。(普通链表的创建可见链表(一)用Java语言实现单向链表(有/无虚拟头节点))add方法:@Override public void add(int index, E element) { //考虑边界 if(index==0){原创 2021-02-18 22:12:59 · 1295 阅读 · 0 评论 -
链表(二)用Java语言实现双向链表
单向链表与双向链表单向链表结构:双向链表结构:在之前的文章中已经完成了对单向链表的实现:用Java语言实现单向链表(有/无虚拟头节点) ,那么实现双向链表只需要在单链表的基础上做一些更改即可。改动位置改动操作private Node < E > last为LinkedList添加尾节点private Node < E > prev为节点Node添加前驱节点private Node node(int index)私有方法,获取索引为in原创 2021-02-18 16:28:21 · 239 阅读 · 0 评论 -
链表(一)用Java语言实现单向链表(有/无虚拟头节点)
接口设计在动态数组ArrayList和链表LinkedList中,有很多相同功能但不同实现的方法,因此将这些方法提取出来构成一个接口List,用于给这两个类实现。方法功能int size()返回元素数量boolean isEmpty()是否为空boolean contains(E element)是否包含element元素int indexOf(E element)返回element元素(可以为空)在数组中的索引,不存在则返回-1void clear(原创 2021-02-10 22:53:45 · 211 阅读 · 1 评论 -
动态数组ArrayList的实现(添加缩容功能)
ArrayListpublic class ArrayList<E> { //数组大小 private int size=0; //元素数组 private E[] elements; //常量 private static final int DEFAULT_CAPACITY=10; private static final int ELEMENT_NOT_FOUND=-1; public ArrayList(int cap原创 2021-02-08 16:11:20 · 236 阅读 · 0 评论