
数据结构
文章平均质量分 62
鸭梨山大哎
life hard take it easy
展开
-
InnoDB 的索引模型
在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。又因为前面我们提到的,InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。每一个索引在 InnoDB 里面对应一棵 B+ 树。假设,我们有一个主键列为 ID 的表,表中有字段 k,并且在 k 上有索引。这个表的建表语句是:mysql> create table T(id int primary key, k int not null, name varchar(16),ind原创 2021-07-06 21:00:02 · 144 阅读 · 0 评论 -
索引的常见模型
索引的出现是为了提高查询效率,但是实现索引的方式却有很多种,所以这里也就引入了索引模型的概念。可以用于提高读写效率的数据结构很多,这里我先给你介绍三种常见、也比较简单的数据结构,它们分别是哈希表、有序数组和搜索树。下面我主要从使用的角度,为你简单分析一下这三种模型的区别。哈希表是一种以键 - 值(key-value)存储数据的结构,我们只要输入待查找的键即 key,就可以找到其对应的值即 Value。哈希的思路很简单,把值放在数组里,用一个哈希函数把 key 换算成一个确定的位置,然后把 value 放原创 2021-07-06 20:49:02 · 1149 阅读 · 1 评论 -
堆的应用之优先级队列
优先级队列,顾名思义,它首先应该是一个队列。我们前面讲过,队列最大的特性就是先进先出。不过,在优先级队列中,数据的出队顺序不是先进先出,而是按照优先级来,优先级最高的,最先出队。如何实现一个优先级队列呢?方法有很多,但是用堆来实现是最直接、最高效的。这是因为,堆和优先级队列非常相似。一个堆就可以看作一个优先级队列。很多时候,它们只是概念上的区分而已。往优先级队列中插入一个元素,就相当于往堆中插入一个元素;从优先级队列中取出优先级最高的元素,就相当于取出堆顶元素。举个例子1. 合并有序小文件假设我们原创 2021-05-30 10:14:39 · 418 阅读 · 0 评论 -
堆排序入门
如何建堆?_鸭梨的博客-优快云博客建堆结束之后,数组中的数据已经是按照大顶堆的特性来组织的。数组中的第一个元素就是堆顶,也就是最大的元素。我们把它跟最后一个元素交换,那最大元素就放到了下标为 n 的位置。这个过程有点类似上面讲的“删除堆顶元素”的操作,当堆顶元素移除之后,我们把下标为 n 的元素放到堆顶,然后再通过堆化的方法,将剩下的 n−1 个元素重新构建成堆。堆化完成之后,我们再取堆顶的元素,放到下标是 n−1 的位置,一直重复这个过程,直到最后堆中只剩下标为 1 的一个元素,排序工作就完成了。原创 2021-05-29 15:42:14 · 359 阅读 · 0 评论 -
如何建堆?
我们首先将数组原地建成一个堆。所谓“原地”就是,不借助另一个数组,就在原数组上操作。建堆的过程,有两种思路。第一种是借助我们前面讲的,在堆中插入一个元素的思路。尽管数组中包含 n 个数据,但是我们可以假设,起初堆中只包含一个数据,就是下标为 1 的数据。然后,我们调用前面讲的插入操作,将下标从 2 到 n 的数据依次插入到堆中。这样我们就将包含 n 个数据的数组,组织成了堆。第二种实现思路,跟第一种截然相反,也是我这里要详细讲的。第一种建堆思路的处理过程是从前往后处理数组数据,并且每个数据插入堆中时,都原创 2021-05-29 15:33:26 · 11432 阅读 · 2 评论 -
如何实现一个堆?
什么是堆?以最小堆为例是一种二叉树最小值存储在顶端父节点数值 < 子节点数值 (如不符合就交换父子节点的位置)堆有哪些操作?取最小值 复杂度O(1) 注:等价于删除了添加数据 复杂度O(logn)为什么需要堆?有哪些应用?排序参考堆内存和数据结构堆之间的关系是什么? - 知乎https://www.zhihu.com/question/276016774...原创 2021-05-09 17:18:14 · 1536 阅读 · 1 评论 -
如何理解堆?
堆是一种特殊的树。我们现在就来看看,什么样的树才是堆。我罗列了两点要求,只要满足这两点,它就是一个堆。堆是一个完全二叉树;堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值。我分别解释一下这两点。第一点,堆必须是一个完全二叉树。还记得我们之前讲的完全二叉树的定义吗?完全二叉树要求,除了最后一层,其他层的节点个数都是满的,最后一层的节点都靠左排列。第二点,堆中的每个节点的值必须大于等于(或者小于等于)其子树中每个节点的值。实际上,我们还可以换一种说法,堆中每个节点的值都大于等于(或原创 2021-05-29 15:00:25 · 276 阅读 · 0 评论 -
插入排序的python实现
插入排序的思想把数组分为两部分,已排序和未排序部分(怎么分?)从未排序部分拿数逐个与已排序部分比较,然后放到合适的位置实现# 从第一个元素开始,该元素可以认为已经被排序# 取出下一个元素,在已经排序的元素序列中从后向前扫描# 如果该元素(已排序)大于新元素,将该元素移到下一位置# 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置# 将新元素插入到该位置后# 重复步骤2~5def insertionSort(arr): for i in range(1, len(arr原创 2021-05-29 11:53:15 · 110 阅读 · 0 评论 -
快排算法入门
我们再来看快速排序算法(Quicksort),我们习惯性把它简称为“快排”。快排利用的也是分治思想。乍看起来,它有点像归并排序,但是思路其实完全不一样。我们待会会讲两者的区别。现在,我们先来看下快排的核心思想。快排的思想是这样的:如果要排序数组中下标从 p 到 r 之间的一组数据,我们选择 p 到 r 之间的任意一个数据作为 pivot(分区点)我们遍历 p 到 r 之间的数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后,数组 p 到 r原创 2021-05-29 10:55:44 · 452 阅读 · 0 评论 -
归并排序稳定性分析
归并排序稳不稳定关键要看 merge() 函数,也就是两个有序子数组合并成一个有序数组的那部分代码。在合并的过程中,如果 A[p…m]和 A[m+1…r]之间有值相同的元素,那我们可以像伪代码中那样,先把 A[p…q]中的元素放入 tmp 数组。这样就保证了值相同的元素,在合并前后的先后顺序不变。所以,归并排序是一个稳定的排序算法。def merge(lst1, lst2): # 有序列表lst1 lst2 合并成lst3 lst3 = [] i1, i2 = 0, 0 # 追踪每个列原创 2021-05-29 10:47:34 · 5555 阅读 · 1 评论 -
归并排序的空间复杂度
归并排序的时间复杂度任何情况下都是 O(nlogn),看起来非常优秀。(待会儿你会发现,即便是快速排序,最坏情况下,时间复杂度也是 O(n^2)。)但是,归并排序并没有像快排那样,应用广泛,这是为什么呢?因为它有一个致命的“弱点”,那就是归并排序不是原地排序算法。这是因为归并排序的合并函数,在合并两个有序数组为一个有序数组时,需要借助额外的存储空间。这一点你应该很容易理解。那我现在问你,归并排序的空间复杂度到底是多少呢?是 O(n),还是 O(nlogn),应该如何分析呢?如果我们继续按照分析递归时间复原创 2021-05-29 10:11:08 · 21531 阅读 · 10 评论 -
归并排序之合并步骤
接上文 归并排序之拆分详解_鸭梨的博客-优快云博客你可能已经发现了,merge(A[p...r], A[p...q], A[q+1...r]) 这个函数的作用就是,将已经有序的 A[p...q]和 A[q+1....r]合并成一个有序的数组,并且放入 A[p....r]。那这个过程具体该如何做呢?如图所示,我们申请一个临时数组 tmp,大小与 A[p...r]相同。我们用两个游标 i 和 j,分别指向 A[p...q]和 A[q+1...r]的第一个元素。比较这两个元素 A[i]和 A[j],如果原创 2021-05-29 09:17:47 · 884 阅读 · 0 评论 -
归并排序之拆分详解
核心思想如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。归并排序使用的就是分治思想。分治,顾名思义,就是分而治之,将一个大问题分解成小的子问题来解决。小的子问题解决了,大问题也就解决了。从我刚才的描述,你有没有感觉到,分治思想跟我们前面讲的递归思想很像。是的,分治算法一般都是用递归来实现的。分治是一种解决问题的处理思想,递归是一种编程技巧,这两者并不冲突。前面我通过举例让你对归并有了一个感性的认识,又告诉你,归并原创 2021-05-29 09:07:07 · 743 阅读 · 0 评论 -
如何选择合适的排序算法?
如果要实现一个通用的、高效率的排序函数,我们应该选择哪种排序算法?我们先回顾一下前面讲过的几种排序算法。我们前面讲过,线性排序算法的时间复杂度比较低,适用场景比较特殊。所以如果要写一个通用的排序函数,不能选择线性排序算法。如果对小规模数据进行排序,可以选择时间复杂度是 O(n^2) 的算法;如果对大规模数据进行排序,时间复杂度是 O(nlogn) 的算法更加高效。所以,为了兼顾任意规模数据的排序,一般都会首选时间复杂度是 O(nlogn) 的排序算法来实现排序函数。时间复杂度是 O(nlogn) 的原创 2021-05-29 08:49:23 · 665 阅读 · 0 评论 -
选择排序两层遍历的目的
思想选择排序思想:第一次从arr[0]~ arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]到 arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]到arr[n-1]中选取最小值,与arr[2]交换, .,第i次从arr[i-1]^ arr[n-1]中选取最小值,与arr[i-1]交换,.,第n-1次从arr[n-2]到 arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。python 实现原创 2021-05-25 22:18:02 · 341 阅读 · 1 评论 -
选择排序入门
选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。a = [2, 40, 74, 10, 60, 50]n = len(a)for i in range(n): min_idx = i # 最小值索引 for j in range(i + 1, n): # 从未排序的部分找出最小值,并记住其索引 if a[j] < a[min_idx]: mi原创 2021-05-25 21:47:52 · 132 阅读 · 1 评论 -
插入排序三问
第一,插入排序是原地排序算法吗?从实现过程可以很明显地看出,插入排序算法的运行并不需要额外的存储空间,所以空间复杂度是 O(1),也就是说,这是一个原地排序算法。第二,插入排序是稳定的排序算法吗?在插入排序中,对于值相同的元素,我们可以选择将后面出现的元素,插入到前面出现元素的后面,这样就可以保持原有的前后顺序不变,所以插入排序是稳定的排序算法。第三,插入排序的时间复杂度是多少?如果要排序的数据已经是有序的,我们并不需要搬移任何数据。如果我们从尾到头在有序数据组里面查找插入位置,每次只需要比较一个原创 2021-05-25 21:42:26 · 168 阅读 · 1 评论 -
插入排序入门
我们先来看一个问题。一个有序的数组,我们往里面添加一个新的数据后,如何继续保持数据有序呢?很简单,我们只要遍历数组,找到数据应该插入的位置将其插入即可。这是一个动态排序的过程,即动态地往有序集合中添加数据,我们可以通过这种方法保持集合中的数据一直有序。而对于一组静态数据,我们也可以借鉴上面讲的插入方法,来进行排序,于是就有了插入排序算法。那插入排序具体是如何借助上面的思想来实现排序的呢?首先,我们将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。原创 2021-05-25 21:38:30 · 147 阅读 · 0 评论 -
冒泡排序三问
a=[2,40,74,10,60,50]n=len(a)for i in range(n): for j in range(n-i-1): if a[j]>a[j+1]: a[j],a[j+1]=a[j+1],a[j]print(a) # [2, 10, 40, 50, 60, 74]第一,冒泡排序是原地排序算法吗?冒泡的过程只涉及相邻数据的交换操作,只需要常量级的临时空间,所以它的空间复杂度为 O(1),是一个原地排序算法。第二,冒泡排原创 2021-05-25 21:32:44 · 155 阅读 · 0 评论 -
排序算法的稳定性
什么是稳定性稳定性。这个概念是说,如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。案例我通过一个例子来解释一下。比如我们有一组数据 2,9,3,4,8,3,按照大小排序之后就是 2,3,3,4,8,9。这组数据里有两个 3。经过某种排序算法排序之后,如果两个 3 的前后顺序没有改变,那我们就把这种排序算法叫作稳定的排序算法;如果前后顺序发生变化,那对应的排序算法就叫作不稳定的排序算法。为什么要考察排序的稳定性?很多数据结构和算法课程,在讲排序的时候,都是用整数来原创 2021-05-25 21:25:29 · 1072 阅读 · 0 评论 -
如何分析一个“排序算法”?
学习排序算法,我们除了学习它的算法原理、代码实现之外,更重要的是要学会如何评价、分析一个排序算法。那分析一个排序算法,要从哪几个方面入手呢?排序算法的执行效率对于排序算法执行效率的分析,我们一般会从这几个方面来衡量:1. 最好情况、最坏情况、平均情况时间复杂度我们在分析排序算法的时间复杂度时,要分别给出最好情况、最坏情况、平均情况下的时间复杂度。除此之外,你还要说出最好、最坏时间复杂度对应的要排序的原始数据是什么样的。为什么要区分这三种时间复杂度呢?第一,有些排序算法会区分,为了好对比,所以我们最原创 2021-05-25 21:18:51 · 150 阅读 · 0 评论 -
如何基于链表实现 LRU 缓存淘汰算法?
什么是LRULRU(Least recentlyused,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。我们维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们从链表头开始顺序遍历链表。如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。如果此数据没有在缓存链表中,又可以分为两种情况:如果此时缓存未满,则将原创 2021-05-24 22:45:58 · 192 阅读 · 0 评论 -
单链表插入时间复杂度分析
链表通过指针将一组零散的内存块串联在一起。其中,我们把内存块称为链表的“结点”。为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要记录链上的下一个结点的地址。如图所示,我们把这个记录下个结点地址的指针叫作后继指针 next。从我画的单链表图中,你应该可以发现,其中有两个结点是比较特殊的,它们分别是第一个结点和最后一个结点。我们习惯性地把第一个结点叫作头结点,把最后一个结点叫作尾结点。其中,头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个原创 2021-05-24 22:35:34 · 22348 阅读 · 3 评论 -
链表的存储结构
为了直观地对比,我画了一张图。从图中我们看到,数组需要一块连续的内存空间来存储,对内存的要求比较高。如果我们申请一个 100MB 大小的数组,当内存中没有连续的、足够大的存储空间时,即便内存的剩余总可用空间大于 100MB,仍然会申请失败。而链表恰恰相反,它并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用,所以如果我们申请的是 100MB 大小的链表,根本不会有问题。总结链表不需要连续的内存空间...原创 2021-05-24 22:26:01 · 1879 阅读 · 0 评论 -
全排列的时间复杂度
我们在高中的时候都学过排列组合。“如何把 n 个数据的所有排列都找出来”,这就是全排列的问题。我来举个例子。比如,1,2,3 这样 3 个数据,有下面这几种不同的排列:1, 2, 31, 3, 22, 1, 32, 3, 13, 1, 23, 2, 1如何编程打印一组数据的所有排列呢?这里就可以用递归来实现。如果我们确定了最后一位数据,那就变成了求解剩下 n−1 个数据的排列问题。而最后一位数据可以是 n 个数据中的任意一个,因此它的取值就有 n 种情况。所以,“n 个数据的排列”问题,就可原创 2021-05-24 22:21:40 · 8201 阅读 · 1 评论 -
斐波那契数列的时间复杂度
int f(int n) { if (n == 1) return 1; if (n == 2) return 2; return f(n-1) + f(n-2);}这样一段代码的时间复杂度是多少呢?你可以先试着分析一下,然后再来看,我是怎么利用递归树来分析的。我们先把上面的递归代码画成递归树,就是下面这个样子:这棵递归树的高度是多少呢?f(n) 分解为 f(n−1) 和 f(n−2),每次数据规模都是 −1 或者 −2,叶子节点的数据规模是 1 或者 2。所以,从根节点走到叶子节点.原创 2021-05-24 22:14:26 · 4311 阅读 · 0 评论 -
归并排序的时间复杂度
归并排序算法你还记得吧?它的递归实现代码非常简洁。现在我们就借助归并排序来看看,如何用递归树,来分析递归代码的时间复杂度。归并排序每次会将数据规模一分为二。我们把归并排序画成递归树,就是下面这个样子:因为每次分解都是一分为二,所以代价很低,我们把时间上的消耗记作常量 1。归并算法中比较耗时的是归并操作,也就是把两个子数组合并为大数组。从图中我们可以看出,每一层归并操作消耗的时间总和是一样的,跟要排序的数据规模有关。我们把每一层归并操作消耗的时间记作 n。现在,我们只需要知道这棵树的高度 h,用高度原创 2021-05-24 22:03:55 · 11672 阅读 · 0 评论 -
为什么说红黑树是“近似平衡”的?
我们前面也讲到,平衡二叉查找树的初衷,是为了解决二叉查找树因为动态更新导致的性能退化问题。所以,“平衡”的意思可以等价为性能不退化。“近似平衡”就等价为性能不会退化得太严重。二叉查找树很多操作的性能都跟树的高度成正比。一棵极其平衡的二叉树(满二叉树或完全二叉树)的高度大约是 log2n,所以如果要证明红黑树是近似平衡的,我们只需要分析,红黑树的高度是否比较稳定地趋近 log2n 就好了红黑树的高度不是很好分析,我带你一步一步来推导。首先,我们来看,如果我们将红色节点从红黑树中去掉,那单纯包含黑色节点的原创 2021-05-24 21:48:53 · 530 阅读 · 0 评论 -
什么是红黑树
红黑树的英文是“Red-Black Tree”,简称 R-B Tree。它是一种不严格的平衡二叉查找树,我前面说了,它的定义是不严格符合平衡二叉查找树的定义的。那红黑树究竟是怎么定义的呢?(1)每个节点或者是黑色,或者是红色。(2)根节点是黑色。(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果一个节点是红色的,则它的子节点必须是黑色的。(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。(01) 特性(3)中的叶原创 2021-05-23 15:08:16 · 201 阅读 · 0 评论 -
图的表示:如何存储微博、微信等社交网络中的好友关系
微博、微信、LinkedIn 这些社交软件我想你肯定都玩过吧。在微博中,两个人可以互相关注;在微信中,两个人可以互加好友。那你知道,如何存储微博、微信等这些社交网络的好友关系吗?这就要用到我们今天要讲的这种数据结构:图。图(Graph)。和树比起来,这是一种更加复杂的非线性表结构。我们知道,树中的元素我们称为节点,图中的元素我们就叫做顶点(vertex)。从我画的图中可以看出来,图中的一个顶点可以与任意其他顶点建立连接关系。我们把这种建立的关系叫做边(edge)。我们生活中就有很多符合图这种结构的例原创 2021-05-23 14:47:16 · 378 阅读 · 0 评论 -
拓扑排序:如何确定代码源文件的编译依赖关系
什么是拓扑排序?由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序拓扑排序有何应用?我们知道,一个完整的项目往往会包含很多代码源文件。编译器在编译整个项目的时候,需要按照依赖关系,依次编译每个源文件。比如,A.cpp 依赖 B.cpp,那在编译的时候,编译器需要先编译 B.cpp,才能编译 A.cpp。编译器通过分析源文件或者程序员事先写好的编译配置文件(比如 Makefile 文件),来获取这种局部的依赖关系。那编译器又该如何通过源文件两两之间的局部依赖关系,确定一个全局的编原创 2021-05-23 14:26:25 · 699 阅读 · 0 评论 -
并行算法:如何利用并行处理提高算法的执行效率?
时间复杂度是衡量算法执行效率的一种标准。但是,时间复杂度并不能跟性能划等号。在真实的软件开发中,即便在不降低时间复杂度的情况下,也可以通过一些优化手段,提升代码的执行效率。毕竟,对于实际的软件开发来说,即便是像 10%、20% 这样微小的性能提升,也是非常可观的。算法的目的就是为了提高代码执行的效率。那当算法无法再继续优化的情况下,我们该如何来进一步提高执行效率呢?我们今天就讲一种非常简单但又非常好用的优化方法,那就是并行计算。今天,我就通过几个例子,给你展示一下,如何借助并行计算的处理思想对算法进行改造原创 2021-05-23 14:06:08 · 2719 阅读 · 0 评论 -
回溯算法入门
在我们的一生中,会遇到很多重要的岔路口。在岔路口上,每个选择都会影响我们今后的人生。有的人在每个岔路口都能做出最正确的选择,最后生活、事业都达到了一个很高的高度;而有的人一路选错,最后碌碌无为。如果人生可以量化,那如何才能在岔路口做出最正确的选择,让自己的人生“最优”呢?我们可以借助前面学过的贪心算法,在每次面对岔路口的时候,都做出看起来最优的选择,期望这一组选择可以使得我们的人生达到“最优”。但是,我们前面也讲过,贪心算法并不一定能得到最优解。那有没有什么办法能得到最优解呢?2004 年上映了一部非常著原创 2021-05-23 11:56:05 · 144 阅读 · 0 评论 -
redis中用到的数据结构
Redis 数据库介绍Redis 是一种键值(Key-Value)数据库。相对于关系型数据库(比如 MySQL),Redis 也被叫作非关系型数据库。像 MySQL 这样的关系型数据库,表的结构比较复杂,会包含很多字段,可以通过 SQL 语句,来实现非常复杂的查询需求。而 Redis 中只包含“键”和“值”两部分,只能通过“键”来查询“值”。正是因为这样简单的存储结构,也让 Redis 的读写效率非常高。除此之外,Redis 主要是作为内存数据库来使用,也就是说,数据是存储在内存中的。尽管它经常被用作内原创 2021-05-23 11:21:48 · 244 阅读 · 0 评论 -
缓冲池、顺序读取与随机读取
根据存储介质的不同,可以将数据库分为基于磁盘的数据库系统、基于内存的数据库系统,以及混合型数据库系统。基于磁盘的数据库系统(disk-base database)是最为常见的一种关系型数据库,比如MySQL、Oracle、SQL Server、DB2数据库。随着内存容量的不断增加,基于内存的数据库系统(in-memory database)也变得十分流行,MySQL NDB Cluster,Oracle Ten Times等数据库厂商都提供了内存关系型数据库系统。而混合型数据库系统(hybrid datab原创 2021-05-23 10:37:46 · 2316 阅读 · 2 评论 -
Mysql 索引是如何实现的?
数据库索引是如何实现的呢?底层使用的是什么数据结构和算法呢?算法解析思考的过程比结论更重要。1. 解决问题的前提是定义清楚问题如何定义清楚问题呢?除了对问题进行详细的调研,还有一个办法,那就是,通过对一些模糊的需求进行假设,来限定要解决的问题的范围。如果你对数据库的操作非常了解,针对我们现在这个问题,你就能把索引的需求定义得非常清楚。但是,对于大部分软件工程师来说,我们可能只了解一小部分常用的 SQL 语句,所以,这里我们假设要解决的问题,只包含这样两个常用的需求:根据某个值查找数据,比如 se原创 2021-05-23 10:13:59 · 2321 阅读 · 0 评论 -
跳表入门
什么是跳表?链表+ 多级索引为什么需要跳表?对于一个单链表来讲,即便链表中存储的数据是有序的,如果我们要想在其中查找某个数据,也只能从头到尾遍历链表。这样查找效率就会很低,时间复杂度会很高,是 O(n)。那怎么来提高查找效率呢?如果像图中那样,对链表建立一级“索引”,查找起来是不是就会更快一些呢?每两个结点提取一个结点到上一级,我们把抽出来的那一级叫做索引或索引层。你可以看我画的图。图中的 down 表示 down 指针,指向下一级结点。如果我们现在要查找某个结点,比如 16。我们可以先在索原创 2021-05-23 09:39:25 · 149 阅读 · 0 评论 -
外排序入门
什么是外排序?外排序是指当待排序的数据量较大、无法一次性全部存入内存中执行排序操作时,先将待排序的数据存储在外存储器上,并将其划分为多个数据块,分批次读入内存中执行排序操作的过程,在这一排序过程中需经过多次内、外存储器之间的数据交换,才可完成排序操作。外排序的基本方法外排序通常使用归并排序,其排序过程大体可划分为3个阶段。1.文件读入阶段由于文件较大,我们首先需根据内存的大小,将文件划分为若干个子文件或段,依次读入内存中。2.子文件排序阶段读入内存后,我们可利用内排序中的方法对每个子文件原创 2021-05-23 09:07:14 · 350 阅读 · 0 评论 -
最长公共子序列
最长公共子序列问题就是:在给定的两个序列里,可以作为两个序列的子序列的最长的序列是什么。比如,考虑两个单词abracadabra和batter作为两个字母序列,这两个序列的最长公共子序列就是bar。在这个问题里,可以有多个具有相同最大长度的公共序列存在,但在这个例子里,bar是唯一一个长度为3的公共子序列。用暴力算法来解决这个问题可以通过查看一个序列的所有子序列是不是另一个序列的子序列,并且一直执行下去直到找到最长的子序列。对于长度为n的序列来说,它的子序列的数量为2^n。要得到这个结果,可以把.原创 2021-05-22 17:35:40 · 230 阅读 · 0 评论 -
动态规划入门
动态规划(Dynamic programming)是另一种经常被用来解决最优化问题的技术,它有点类似于分治算法。当我们把问题分解成子问题,并且每个子问题都只出现一次的时候,简单的分治算法会非常有效。但是,这种策略对于递归版本的斐波那契算法是非常低效的,这是因为它会让我们对相同的子问题进行多次重复的计算。动态规划解决这个问题的方案是:把每个子问题的答案都存起来,并在需要的时候重用这个存起来的答案,而不是去重新计算这个子问题。我们的迭代版本的斐波那契算法就可以被归类为动态规划算法。作为提醒,下面这个代码.原创 2021-05-22 17:12:33 · 251 阅读 · 0 评论