![](https://img-blog.csdnimg.cn/20201014180756928.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
算法自学笔记
Raine_Yang
这个作者很懒,什么都没留下…
展开
-
算法自学笔记:红黑查找树(2)程序实现
红黑树的所有搜索操作,包括get() min() max() ceiling() floor() rank() select() 等(参数未列出),均和普通二叉树一样,搜索时无视节点颜色即可。红黑树主要在于其插入操作,上一篇我们讲了对双节点插入的几种情况(文章链接)我们会发现即使最复杂的情况,插入值在两值中间,也可以转化为基础操作。下图展示了红黑树插入所有可能的情况:插入值小于左端值,插入值在两值中间,插入值大于右端值。我们可以发现其实所有情况都可以以下列规律化解:1 如红键在右节点,左旋2 如红原创 2022-02-12 14:08:56 · 626 阅读 · 0 评论 -
算法自学笔记:平衡查找树(2)红黑查找树
红黑查找树是2-3树概念的一个实现。把2-3树中的3节点以一个小的二分查找树实现。代表3节点的树左链接被标记为“红”链接。红黑查找树性质:没有一个节点有两条红链接(不存在4节点)从根节点到树底黑节点数量一致红节点始终在左边红黑树搜索操作:搜索操作忽视链接颜色,和普通二叉树搜索代码完全一致。同理,其他查找操作,如ceiling, selection, rank,均和二叉树代码一致红黑树区别主要在插入操作。要实现插入首先需要实现三个基础操作:左旋,右旋和变色左旋:如果红链接在右侧,要将其挪回到左原创 2022-02-08 12:45:59 · 578 阅读 · 0 评论 -
算法自学笔记:平衡查找树:2-3树
传统的二分查找树随机的插入与删除操作会逐渐使得树趋于不平衡,时间复杂度无法保证在对数级别。要保证各操作时间复杂度为O(logN),我们需要做的是让树完全平衡,即从根节点沿各路径到树底的高度一样。平衡查找树这一章节分三个部分:2-3树,红黑查找树,B树2-3查找树在2-3查找树里,我们有两种类型的节点,一种是和普通二叉树一样的包含一个值,两个子节点的节点。另一种节点包含两个值,三个子节点。其中,一个子节点链接均小于两个值的值,一个子节点链接在两值之间的数值,一个子节点链接大于两值的数值。搜索操作:和原创 2022-02-05 16:33:14 · 567 阅读 · 0 评论 -
算法自学笔记:二分查找树(3)删除操作
要完全实现符号表,我们还需要实现删除一个键-值对一个笨办法是直接把该键对应的值设为null,但是键还保留在树中。显然,这么做会导致大量无用的键占用内存如何彻底删除键-值对?我们可以先从一个简单问题入手:删除最小值或最大值1 删除最小/最大值// delete the minimum value public void deleteMin() { root = deleteMin(root); } private Node deleteMin(Node x) { if (x.left原创 2022-02-04 17:14:05 · 965 阅读 · 0 评论 -
算法自学笔记:二分查找树搜索操作(2)
本文我们继续搭建二叉树剩下一部分方法API:size() 返回总节点数select(int k) 返回树中排名为k的键,即有k个键小于它rank(Key key) 返回给定键的排名,即树中小于该键的数量要实现这些方法,我们首先需要对各节点进行一个计数,找出各节点的子节点数目(包括其本身)。我们在Node构造方法里加入count作为计数器,并在put方法里更新各节点计数 private class Node { private Key key; private Value val原创 2022-02-04 15:49:20 · 693 阅读 · 0 评论 -
算法自学笔记:二分查找树插入和比较操作实现(1)
本文包括二分查找树基本插入和查找操作API:put(Key key, Value value) 插入一个键-值对get(Key key) 根据一个键返回对应值,如不存在返回nullmin() 返回最小键max() 返回最大键ceiling(Key k) 返回大于k的最小键floor(Key k) 返回小于k的最大键二分查找树作为递归数据结构。尽管也可以使用循环进行查找,最好使用递归方法进行查找。在这里我们使用递归函数两个基本构成:基线条件,状态转移方程,进行说明1 put// p原创 2022-01-31 16:30:11 · 596 阅读 · 0 评论 -
算法自学笔记:二分查找树
我们之前讲二叉堆的时候使用较不直观的方式实现树的数据结构:使用一个数组保存各节点,索引为k节点父节点为k/2,子节点为k * 2, k * 2 + 1这一次,我们使用直观方式实现树结构:构造实际的节点类。类似于链表里的节点,不过每一个节点指向两个节点:left和rightprivate Class Node { private Value value; private Key key; // Value and Key are generic variables for symbol tables原创 2022-01-28 16:10:07 · 800 阅读 · 0 评论 -
算法自学笔记:符号表(2)基础实现
符号表有两个基础的实现方法,简单易行,但是性能较差1 链表实现以链表储存符号表,每一个链表储存一个键及对应值。查找:从头遍历链表,直到找到目标键,读取对应值。时间复杂度O(N)插入:从头找到末尾,插入新的键-值对。时间复杂度O(N)2 数组实现数组支持随机访问。因此如果数组各元素有序排布可以使用二分查找进行快速查找该方法构建两个等大数组分别储存键与值,每一个键和其对应的值在数组里索引相同查找:使用二分查找找出键对应索引,根据索引在值数组里查到目标值,时间复杂度O(logN)插入:添加一个键原创 2022-01-20 23:00:01 · 598 阅读 · 0 评论 -
算法自学笔记:符号表(1) API
1 符号表基本API符号表(symbol table)是一种将每一个值(value)和一个特定键(key)匹配的数据结构。这一“键-值”匹配有很广泛的应用。如DNS解析要根据给出URL(键)找出对应IP(值)。要实现符号表,需要实现两个基本操作:1 put() 插入一个值和其对应的键2 get() 找出对应一个键的值这一点可以类比数组。数组从某种程度上讲就是一个符号表,其中对应的键是从0开始的正整数,我们可以通过访问键获得数组该位置的数值。因此,符号表的查找就类似于 a[key] = value原创 2022-01-13 22:40:08 · 258 阅读 · 0 评论 -
算法自学笔记:堆排序
利用二叉堆的有序性质,可以利用其进行排序。堆排序分为以下两步:1 重排列元素数组构成一个二叉堆。2 不断返回最大元素(即根节点)直到堆为空注:对于堆的方法可参考文章https://blog.csdn.net/Raine_Yang/article/details/1220636241 从下至上构造堆当然,我们可以对每个元素调用之前实现的insert()方法,但是这么做每一次定位都需要查找整个堆。一个更高效的查找方法是自下而上。如图,我们可以把E-L-E这一部分当做一个子堆。我们逐层对各个子堆进行原创 2021-12-26 17:22:43 · 616 阅读 · 0 评论 -
算法自学笔记:二叉堆实现优先队列
二叉堆是一种特殊的二叉树,满足两个条件:1 二叉堆完全平衡,就是说除了最底层,上面都完全填满2 二叉堆节点有序,对于每一个父节点都大于等于子节点。尽管书上一般用图表示二叉堆,但是在计算机里实现二叉堆并不需要真正构造节点和边,而是构造一个数组,每个元素代表节点,而索引之间代数关系代表链接。二叉堆的性质:1 a[1](根节点)为最大值2 使用数组索引在堆上移动: 对于k索引节点: 父节点 k / 2子节点2k 2k + 1...原创 2021-12-21 16:34:28 · 384 阅读 · 0 评论 -
算法自学笔记:系统排序
排序算法在程序开发中有大量应用。有时是直接使用,如对音乐播单里的歌曲进行排序,有时是使用排序简化问题,如convex hull 问题中使用排序优化比暴力求解快很多,而有时是不明显的应用。java语言提供了封装好的排序方法 Arrays.sort() 该方法接受基础数据类型和实现Comparable 或 Comparator的对象。其参数为要进行排序的数组,也可以加上指定的Comparator 该方法对于基础数据类型使用快速排序以提高速度,而对于对象进行归并排序以保证稳定性和保证nlogn时间下限。三向切原创 2021-12-05 10:44:26 · 997 阅读 · 0 评论 -
算法学习笔记:三向切分快速排序
在出现较多重复元素时,对于归并排序性能并不会受到影响,但是对于快速排序时间复杂度会由线性对数级别到达平方级别。出现这一性能下降的原因是对于传统快速排序,每一次切分会将数组分为两部分,这两部分长度越接近效率越高,而在最糟情况,即所有元素值相等,快速排序切分的一部分长度将只有1,导致需要切分N此才能完成,另外每一次分区又要遍历数组,总时间复杂度为for(int i = 0; i < N; i++) {i} (不知道连加符号电脑上怎么打,且拿for循环表示)三向切分的快速排序:解决大量重复键的一个方法原创 2021-11-21 17:05:06 · 971 阅读 · 0 评论 -
算法自学笔记:贪心算法(1)
算法设计需要时刻注意一点,没有可以套用到所有问题的模板算法,每一个问题都要思考其特定的解决方法。贪心算法定义(非专业):迭代执行每一次“最佳”策略,每一步最佳决策可能可以带来整体的最优解。举例:寻找图最短路径的狄克斯特拉算法(Dijkstra Algorithm)贪心算法和分而治之方法的区别1 贪心算法一般更容易想出,毕竟只需要找到每一步最优解,而分治法要找出基线条件,递归条件,状态转移方程,较难设计。2 贪心算法易于分析。贪心算法由于一般可以一次性得到结果,易于预测其时间复杂度。而分治法由于一般原创 2021-11-14 14:16:33 · 428 阅读 · 0 评论 -
算法自学笔记:选择算法
在编程中一个常见问题为选择一个数组第k大的元素,如找最大值,最小值,中位数,第3个元素等。要构建一个选择算法,首先预测一下该算法时间上限即下限:1 一个简单的实现是先对数组排序,时间复杂度最小为O(nlogn),然后直接选择排序后数组第k个元素2 当k很小时可以直接遍历数组找到元素,时间复杂度为O(n)3 任何选择算法时间下限为Ω(n),因为无论任何算法都必须检索数组所有元素才能实现选择依靠递归切分实现的快速选择算法该算法过程如下:1 任取一个位置的元素,把比该元素小的值移到数组左侧,把该元素原创 2021-11-07 16:45:07 · 218 阅读 · 0 评论 -
算法自学笔记:快速排序
快速排序在一般情况下是所有排序算法里面效率最高的,并且属于原地排序(递归需要栈内存为logN,符合原地排序定义)快速排序原理:1 选择数组中一个值为基准值,把所有比基准值小的元素移到其左边,所有大于基准值的元素移到其右边2 将左半数组和右半数组递归进行相同操作,直到数组长为1示例程序private static int partition(Comparable[] a, int lo, int hi) { int i = lo, j = hi; while (true) { whil原创 2021-11-07 16:04:30 · 172 阅读 · 0 评论 -
算法自学笔记:动态规划的基本思想和概念(1)记忆化优化递归程序
一些静态模型,只要人为引入“时间”因素,就可以转化为多阶段动态模型,用动态规划处理。最优化原理(principle of optimality):一个最优策略的子策略,对于它的初态和终态必然是最优的例:斐波那契数列递归方法可以很容易求得,但是效率低下,因为其子状态被重复计算。可以把已经计算过的值记录下来,避免重复计算。package preDP;public class Fibonacci { public static int mod = 1000000007; // use to in原创 2021-10-25 22:32:35 · 142 阅读 · 0 评论 -
算法自学笔记:排序算法的稳定性
排序算法的稳定性指的是经过排序后,大小相同的元素顺序是否发生改变。这一属性有时十分重要,比如,对于一个列车时刻表,我们首先将其按出发时间排序,之后再一次按照编号排序时,我们希望最大程度上保留之前的时间顺序。几种常见排序算法稳定性:1 选择排序:稳定在选择排序中,两个相同元素不会发生交换,因为选择各元素的顺序依照原顺序2 插入排序:不稳定如果一个元素需要进行多次交换才能到达其正确位置,其沿途的相同元素顺序会被颠倒3 希尔排序:不稳定希尔排序就是插入排序升级版,所有原因和插入排序一样4 归并排序原创 2021-10-10 14:57:27 · 387 阅读 · 0 评论 -
java自学笔记:Comparable 和 Comparator
用于构造可以比较的对象主要依靠两种接口:Comparable和Comparator.调用Comparable接口需要实现compareTo方法,compareTo返回一个整数代表比较结果,比较规则需要自己设定。需要注意的是Comparable 只能支持一个比较规则,因此无法实现按对象不同的属性进行不同比较如上图按年,月,日比较日期。该对象调用Comparable 接口,并在内部声明compareTo方法Comparator 可以实现对同一对象多种比较规则。如对于一个书单,实现把里面的书按书名首字母,原创 2021-10-10 14:34:25 · 205 阅读 · 1 评论 -
算法自学笔记:排序算法复杂度
对于依靠比较进行排序的排序算法,判定其时间复杂度一个重要依据为需要的比较次数。时间上限(upper bound 用O表示):某一特定算法解决问题的时间复杂度,如插入排序时间上限为O(N²),归并排序时间上限为O(NlogN)时间下限(lower bound 用Ω表示):对于解决该问题的所有算法可能达到的最优时间复杂度。最优算法 (optimal algorithm 用θ表示):实现以时间下限的复杂度解决该问题的算法,对于该算法O(X) = Ω(X) = θ(X)证明所有依靠比较进行排序的算法时间下限原创 2021-10-07 11:25:39 · 394 阅读 · 0 评论 -
算法自学笔记:自顶向上的归并排序
传统归并排序先递归把原数组拆为子数组,然后对子数组进行合并。还有一种归并排序不需要先拆分,直接对子数组进行合并得到最后的有序数组。程序如下public class ButtomUpMergeSort extends MergeSortImproved { // merge method is inherited from superclass // override sort method public static void sort (Comparable[] a) { int n原创 2021-10-06 19:35:04 · 213 阅读 · 0 评论 -
算法自学笔记:归并排序(1)
归并排序(merge sort)是一种利用分治思想的排序算法,它的时间复杂度在所有完全依靠交换进行排序的排序算法中是最快的归并排序基本过程:1 把数组分为左右两半,再递归对子数组进行二分,直到每个子数组只剩下一个元素。2 对数组进行两两合并,合并方法具体如下:创建一个辅助数组aux并把原数组的元素依次存储在辅助数组变量j,k指向辅助数组中对应原数组两部分([low, mid]和[mid + 1, high])的开头,变量i指向原数组,从0开始判断aux[j]和aux[k]大小,将较小的元素放入原创 2021-10-04 15:51:57 · 162 阅读 · 0 评论 -
算法自学笔记:Convex Hull问题
convex hull是一个计算机几何问题。对于平面上N个坐标不同的点,如何选取并连接其中部分点构成一个多边形使得该平面所有点都被包括在这一多边形内。如图:对于输入的任意一组点,输出构成convex hull的顶点坐标Convex Hull有如下两个几何性质:1 从一点出发进行连接,遍历所有顶点只需要逆时针旋转。2 Convex Hull各个顶点关于y坐标最小的顶点的极距角(polar angle)为递增关系。极距角: 取一点O为投影中心,以一定的半径作一个球,称为投影球;通过球心作一个水平原创 2021-10-01 18:18:22 · 763 阅读 · 0 评论 -
算法自学笔记:洗牌算法
洗牌,及把一个有序的数组打乱,使每个元素位置随机,是一个经典算法问题。一个易于理解的方法是为每个元素分配一个随机数,并按照该随机数进行排序。该方法可以保证元素位置随机,但是时间复杂度取决于排序算法,及最快只能为O(nlogn)有一种洗牌算法可以实现O(n)时间复杂度,叫做Knuth shuffle。该方法从第一个元素开始,每次把当前索引的元素和索引为[0, i]之间一个随机数的元素交换,直到遍历整个数组。示例代码如下// the "sort" here refers to shuffle public原创 2021-09-27 23:33:37 · 173 阅读 · 0 评论 -
算法自学笔记:希尔排序
希尔排序可以理解为插入排序的升级版。在插入排序中,如果发现位置靠后的元素比位置靠前的元素小,会一个个比较元素直到移到正确位置,这一做法会带来频繁比较和交换操作,因而效率低。而希尔排序一次比较跨度大于1,排序一轮后减小比较的跨度,直到等于1.这么做可以使最后一轮插入排序时数组已经基本上排列有序,减少频繁交换。对于按照跨度大小A排序后的数组,再按照跨度B排序时,原来按跨度A交换的各元素位置不会改变(证明很困难,就当定理即可)希尔排序选取合适的区间跨度很重要,一个常用的方法是选取3n + 1作为增幅,还有一个原创 2021-09-20 16:56:03 · 133 阅读 · 0 评论 -
算法自学笔记:永别了,LinkedList
我在之前做算法题的时候经常爱使用java.util.Collection包里面的LinkedList。总是以为LinkedList底层是一个链表,在增删操作较多的情况应该快于底层为数组的ArrayList。但是之前使用LinkedList时,经常出现设计的算法时间复杂度理论上一个为O(N)或者O(NlogN),运行时却往往超过时间要求,而且运行时间的增幅似乎像是平方时间。实际上,LinkedList一大致命缺陷就是它每一个随机查找操作的时间复杂度都O(N),结果导致一些原本为线性时间的由于查找操作硬生生成原创 2021-09-01 23:07:16 · 122 阅读 · 0 评论 -
算法学习笔记:自建集合数据类型(3),使用链表实现队列
使用链表实现队列和之前文章使用链表实现栈的原理基本上类似,唯一不同的是把后进先出改为了先进先出有一点不同的是相较于栈只需要一个first变量来保存头节点,队列还需要一个last变量保存尾节点。每次enqueue都在尾节点添加一个新的元素,dequeue删除头节点元素 public void enqueue(Item item) { Node oldLast = last; last = new Node(); last.item = item; n++; if (n == 1) {原创 2021-08-29 10:25:32 · 104 阅读 · 0 评论 -
算法学习笔记:自建集合数据类型(2),使用链表实现下压栈
链表是一种递归数据结构,它或者为空,或含有泛型元素的节点和指向另一节点的引用通过嵌套类实现链表private class Node { Item item; Node next;}Node作为链表中一个元素,保存一个泛型数据和下一个链表的引用。用例不会用到单独Node,因而设为private通过new Node() 构造函数创建Node对象,调用结果是一个指向Node对象引用,实例变量初始为null。通过把一个Node的next指向下一个元素可以实现链表,最后一个Node的next为n原创 2021-08-29 09:57:41 · 94 阅读 · 0 评论 -
算法学习笔记:自建集合数据类型(1),使用数组实现下压栈
java集合数据类型的一个基本操作是可以迭代遍历(如foreach语句)在可迭代集合数据类型需要实现:1 一个iterator()方法并返回一个Iterator对象2 Iterator类要含有两个方法:hasNext()(返回boolean)next()(返回集合中一个泛型元素)Iterator类在java.util包里,所以开始需要importimport java.util.Iterator;要让这个类可迭代,需要在声明中加上implements Iterable 接口public cl原创 2021-08-26 10:02:45 · 122 阅读 · 0 评论 -
算法图解笔记:贪心算法
贪心算法每一步选择局部最优解,最终可以得到全局最优解贪心算法有时是一种近似算法,如对于背包问题有可能得到的不是最优解,但是十分接近集合覆盖问题代码:原创 2021-08-04 11:10:12 · 424 阅读 · 0 评论 -
算法图解笔记:狄克斯特拉算法
对于非加权图,广度优先搜索可以找出边数最少的路径,不过找出加权图的最短路径要使用狄克斯特拉算法加权图指每条边都有关联数字的图,这些数字被称为权重,没有权重的图即为非加权图。图中从一个节点出发后可以回到该节点的路径称为环绕环的路径会增加权重,一定不是最短路径,无向图任意两个节点都构成环狄克斯特拉算法只能用于有向无环图...原创 2021-07-24 19:22:42 · 315 阅读 · 0 评论 -
算法图解学习笔记:广度优先搜索
图由节点和边组成,一个节点可能和多个节点相连,这些节点成为邻接广度优先搜索回答两个问题:1是否存在A到B路径 2从A到B哪条路径最短广度优先搜索从起点的邻接节点开始,搜索完毕后再搜索其邻接节点的邻接队列queue可以实现广度优先搜索,队列类似栈,不允许随机访问,只支持两个操作:入队和出队。队列先进先出(FIFO),栈后进先出(LIFO)。...原创 2021-07-21 22:03:29 · 179 阅读 · 0 评论