整理不易,如果你觉得这个考前一天就能看完的抱佛脚资料很good
那么,不需要打赏或感谢,关注我或star本项目即可
本项目将会在短期更新本学期新增考点⚠
看完并掌握后达到80分没问题
本人整理的全套思维导图请自行取用,再次欢迎关注我或star本项目
感谢🙇各路大佬的数据结构干货! 如有侵权,请告知我来删除。🎈 欢迎pull request对内容进行补充!🎉(虽然我都不指望会有人看...)
while(p) {
q=p;
p=p->next;
q->next=head->next;
head->next=q;
}
删除一个元素后,队首指针加1,front=(front+1)%6,结果为4,
每插入一个元素,队尾指针加1,即real=(real+1)%6,加入两个元素后变为2
中缀表达式看成一个字符串,从左到右开始扫描中缀表达式;
1.遇到操作数:直接输出(添加到后缀表达式中) 2.栈为空时,遇到运算符,直接入栈 3.遇到左括号:将其入栈 4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,括号不输出。 5.遇到其他运算符:加减乘除:弹出所有优先级大于等于该运算符的栈顶元素,然后将该运算符入栈 6.最终将栈中的元素依次出栈,输出。
首先按照运算的先后顺序将表达式全部都添加上括号
(a+b)*c*(d-e/f)----> (((a+b)*c)*((d-(e/f))))
然后由于是后缀表达式,从里到外将所有运算符都拿到右括号的右边
(((ab)+c)*((d(ef)/)-))*
最后再将所有括号都去掉
ab+c*def/-*
同理,如果是变为前缀表达式的话,就把运算符拿到括号左边就可以啦
已知二维数组A[0..9, 0..9]中,元素a[2][3]
的地址为400,每个元素占4个字节,则元素a[6][5]
的地址为多少?
行优先: $400+4(10\times(6-2)+(5-3))$
列优先: $400+4((6-2)+10\times(5-3))$
性质1 树中的结点数等于所有结点的度数加1。
性质2 度为d的树中第i层上至多有 $d^{i}-1$ 个结点(i≥1)。
性质3 为k的d叉树至多有d^k^/d-1个结点。
性质4 具有n个结点的d叉树的最小深度为 $log_{d}{(n·(d-1)+1)}$
二叉树性质
性质1:二叉树第i层上的结点数目最多为 2^i-1^ (i≥1)。
性质2:深度为k的二叉树至多有2^k^-1个结点(k≥1)。
性质3:包含n个结点的二叉树的高度至少为log~2~ (n+1)。
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1。
完全二叉树性质
①$n=n0+n1+n2$ => $n=2n{0}+n{1}-1$
②$n=1+n{1}+2n{2}$
得出,若完全二叉树有n个结点,n0=$\left \lceil n/2 \right \rceil$
设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边
具有 $n$ 个结点的完全二叉树的深度为 $\left \lceil \log_{2}{(n+1)} \right \rceil$
结点 $i$ 所在的层次为 $\left \lfloor \log_{2}{i} \right \rfloor +1$
深度为 $k$,至少有 $2^{k-1}$ 个节点,最多有 $2^{k}-1$ 个节点
深度为k且有2^k^-1个结点
哈夫曼树并不唯一,但带权路径长度一定是相同的
A:10 B:01 C:0011 D:11 E:000 F:00101 G:00100 那么当我想传送 ABC 时,编码为 10 01 0011
*树中 所有叶子结点的 `路径长度权值` 之和**
最小生成树(MST):权值最小的生成树
构造网的最小生成树必须解决下面两个问题:
普里姆算法(Prim算法)
具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止+
即最大路径长度(权重)的路径,可能不止一条
相关术语 AOV网络(Activity On Vertex Network):有向图,用顶点表示活动,用弧表示活动的先后顺序 AOE网络(Activity On Edge):有向图,用顶点表示事件,用弧表示活动,用权值表示活动消耗时 (带权的有向无环图) 活动:业务逻辑中的行为,用边表示 事件:活动的结果或者触发条件 关键路径:具有最大路径长度(权重)的路径,可能不止一条 活动的两个属性:e(i)最早开始时间,l(i)最晚开始时间 事件的两个属性:ve(j)最早开始时间,vl(j)最晚开始时间
步骤:
①:求 $ve(j)$ 的值(事件最早开始时间)
从前向后,直接前驱节点的ve值+当前节点的边的权值(有可能多条,取最大值
)
第一个顶点的ve等于0
用0来+
顶点 | V1 | V2 | V3 | V4 | V5 | V6 | V7 |
---|---|---|---|---|---|---|---|
ve(j) | 0 | 3 | 2 | 6 | 7 | 5 | 10 |
②:求 $vl(j)$ 的值(事件最迟开始时间)
从最后一个节点开始用10来 减去 前面的各个边(可能要累加) 的值 就能拿到每个点的vl(j)
从后向前(V9开始),直接后继节点的vl值-当前节点的边的权值(有可能多条,取最小值
)
终结点的vl等于它的ve
顶点 | V1 | V2 | V3 | V4 | V5 | V6 | V7 |
---|---|---|---|---|---|---|---|
vl(j) | 0 | 3 | 3 | 6 | 7 | 6 | 10 |
③:求 $e(i)$ 的值(活动最早开始时间)
e ( i ): 活动ai是由弧< vk,vj >表示,则活动的最早开始时间应该和事件vk的最早发生时间相等 因此,就有$e(i)=ve(k)$
即:边(活动)的最早开始时间等于它发出的顶点(事件)的的最早发生时间
就是 每个箭头的 起始点
边 | a1(3) | a2(6) | a3(2) | a4(4) | a5(2) | a6(1) | a7(3) | a8(1) | a9(3) | a10(4) |
---|---|---|---|---|---|---|---|---|---|---|
e(i) | 0 | 0 | 0 | 3 | 3 | 2 | 2 | 6 | 6 | 7 |
④:求 $l(i)$ 的值(活动最晚开始时间) l ( i ):活动ai是由弧< vk,vj >表示,则ai的最晚发生时间要保证vj的最迟发生时间不拖后(vj最迟发生时间为9的话,ai的最迟时间就必须是 9-活动耗时 )。因此,$l(i)=vl(i)-len< v{k},v{j}>$ 即:$边(活动)到达顶点的最晚发生时间 - 边的权重$
边 | a1(3) | a2(6) | a3(2) | a4(4) | a5(2) | a6(1) | a7(3) | a8(1) | a9(3) | a10(4 ) |
---|---|---|---|---|---|---|---|---|---|---|
e(i) | 0 | 0 | 1 | 3 | 4 | 5 | 3 | 6 | 7 | 6 |
⑤:求出关键边和关键路径
当 e(i)==l(i)
,即:活动最早开始时间 = 活动最晚开始时间时,可以得到 关键边 为:
a1 a2 a4 a8 a9
然后 根据关键边组合成关键路径 a1->a4->a9 和 a2->a8->a9
*取关键字的某个线性函数为散列地址,Hash(Key)=Key 或 Hash(Key)= AKey+B**
利用数组下标可以很好的将对应的数据存入哈希表对应的位置。例如:在一个字符串中找出第一次只出现一次的字符,字符串为abcdabcdefg,需要找到e,利用下标统计可以很好地解决这个问题,对于这个问题,你必须开辟对应的256个空间。如果需要查找的数中出现了一个特别大的数(1000000),你必须要开辟1000000个空间,会造成大量空间的浪费。
取关键值被某个不大于散列表长m的数p除后的所得的余数为散列地址。Hash(Key)= Key % P
由于“直接定址法”的缺陷,于是下面引入“除留余数法”,该方法提高的空间的利用率,但不同的Key值经过哈希函数Hash(Key)处理以后可能产生相同的值哈希地址,我们称这种情况为哈希冲突。任意的散列函数都不能避免产生冲突。
哈希表的装填因子
$装填因子 = (哈希表中的记录数) /(哈希表的长度)$
装填因子是哈希表装满程度的标记因子。值越大,填入表中的数据元素越多,产生冲突的可能性越大。
直接使用数组来存储数据。可以想象成一个停车问题。若当前车位已经有车,则你就继续往前开,直到找到下一个为空的车位
1, 2, 3, 4, 5 ... ...
这种多个哈希地址不同的关键字争夺同一个后继哈希地址的现象称为“聚集”。聚集对查找效率有很大影响
±1, ±4, ±9 ... ...
二次探测能有效避免“聚集”现象,但是不能够探测到哈希表上所有的存储单元,但是至少能够探测到一半。
拉链法 的实现比较简单,将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可
对于 线性探测
来说动态调整数组大小是必要的,不然会产生死循环。
拉链法
的删除操作比较方便,直接链表修改地址即可。而 线性探测
删除操作很复杂,而且 线性探测
耗费的内存比拉链法要多
由于哈希表高效的特性,查找或者插入的情况在大多数情况下可以达到O(1),时间主要花在计算hash上,当然也有最坏的情况就是hash值全都映射到同一个地址上,这样哈希表就会退化成链表,查找的时间复杂度变成O(n)
https://blog.csdn.net/SnailMann/article/details/80435311
https://blog.csdn.net/u011240877/article/details/52940469
https://www.cnblogs.com/s-b-b/p/6208565.html
ASL指的是 平均查找时间
关键字序列:(7、8、30、11、18、9、14)
散列函数: H(Key) = (key x 3) MOD 7
装载因子: 0.7
处理冲突:线性探测再散列法
因为现在的数据是7个,填充因子是0.7。所以数组大小=7/0.7=10,即写出来的散列表大小为10,下标从0~9。 第一个元素7,带入散列函数,计算得0。 第二个元素8,带入散列函数,计算得3。 第三个元素30,带入散列函数,计算得6。 第四个元素11,带入散列函数,计算得5。 第五个元素18,带入散列函数,计算得5;此时和11冲突,使用线性探测法,得7。 第六个元素9,带入散列函数,计算得6;此时和30冲突,使用线性探测法,得8。 第七个元素14,带入散列函数,计算得0;此时和7冲突,使用线性探测法,得1。 所以散列表:
地址 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
key | 7 | 14 | 8 | 11 | 30 | 18 | 9 |
所以查找成功的计算: 如果查找7,则需要查找1次。 如果查找8,则需要查找1次。 如果查找30,则需要查找1次。 如果查找11,则需要查找1次。 如果查找18,则需要查找3次:第一次查找地址5,第二次查找地址6,第三次查找地址7,查找成功。 如果查找9,则需要查找3次:第一次查找地址6,第二次查找地址7,第三次查找地址8,查找成功。 如果查找地址14,则需要查找2次:第一次查找地址0,第二次查找地址1,查找成功。 所以,ASL=(1+2+1+1+1+3+3)/ 7=12/ 7
鉴于网络上有各种版本,本人认为此种计算方法比较合理。验证实例可以参考2010年的计算机408考研真题的第一道计算大题和答案。
1. 定义什么叫查找不成功 举个例子来说吧。在已知上面散列表的基础上,如果要查找key为4的关键字。根据散列函数可以计算Hash(key)=Hash(4)=5。此时在地址为5的地方取出那个数字,发现key=11,不等于4。这就说明在装填的时候会发生冲突。根据冲突处理方法,会继续检测地址为6的值,发现key=30,依然不等。这个时候到了地址为6,但是依然没有找到。那么就说明根本就没有key=4这个关键字,说明本次查找不成功。注意:为什么到地址6?因为散列函数中有 mod7 ,对应的地址为0\~6,即0\~6查找失败的查找次数。 再举一个例子。查找key为0的关键字,根据散列函数可以计算Hash(key)=Hash(0)=0。此时在地址为0的地方取出那个数字,发现key=7,不等于0。这就说明在装填的时候会发生冲突。根据冲突处理方法,会继续检测地址为1的值,发现key=14,依然不等。这个时候到了地址为3,发现为空,依然没有找到。所以停止查找,本次查找不成功。因为如果key=0这个关键字存在的话,依照冲突处理函数,就一定能找到它。总不能丢了吧。
2. 根据第一点定义的不成功,依次推下去: 查找地址为0的值所需要的次数为3, 查找地址为1的值所需要的次数为2, 查找地址为2的值所需要的次数为1, 查找地址为3的值所需要的次数为2, 查找地址为4的值所需要的次数为1, 查找地址为5的值所需要的次数为5, 查找地址为6的值所需要的次数为4。 3.计算 查找不成功ASL=(3+2+1+2+1+5+4)/ 7=18/ 7
链地址法
查找成功时的平均查找长度:
ASL = (16+24+31+41)/12 = 7/4
查找不成功时的平均查找长度:
ASL = (4+2+2+1+2+1)/13
注意:查找成功时,分母为 哈希表元素个数
,查找不成功时,分母为 哈希表长度
步骤:
步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
a.将堆顶元素9和末尾元素4进行交换
这里,必须说明一下,所谓的交换,实际上就是把最大值从树里面拿掉了,剩下参与到排序的树,其实只有总结点的个数减去拿掉的节点个数了。所以图中用的是虚线。
b.重新调整结构,使其继续满足堆定义
c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.
后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序
https://blog.csdn.net/u013384984/article/details/79496052
https://www.cnblogs.com/chengxiao/p/6129630.html
我们称这个自堆顶至叶子的调整过程为“筛选”。 从无序序列建立堆的过程就是一个反复“筛选”的过程。
初始化堆的时候是对所有的非叶子结点进行筛选。
最后一个非终端元素的下标是$\left \lfloor n/2 \right \rfloor$,所以筛选只需要从第$\left \lfloor n/2 \right \rfloor$个元素开始,从后往前进行调整。
三数取中
在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。
根据枢纽值进行分割
所有孩子结点串成链表(右子树代表同层的) 放在 左子树(左子树代表有孩子结点)
然后把森林从左到右 连右结点
其实就是还原上面提到的步骤
前提:加入一棵二叉树的根节点有右孩子,则这棵二叉树能够转换为森林,否则转换为一棵树
转换规则:
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的节点。
假设得到的中序序列是有序的。则说明这棵二叉树是二叉排序树
反过来,折半查找 被查找的数组的元素,必须是有序排列的
平衡二叉树又称AVL树。它或者是颗空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1
每个结点的深度相加除以结点个数
最坏情况(深度为N的单支树) 为 $ \frac{N+1}{2}$ (也就是最大值) 最好情况(形态均匀、满二叉树) 和折半查找一样大约为 $log_{2}{N}$ =log2(n+1)-1 PS:若构造完成,例: 则平均查找长度为:(1×1+2×2+3×4+4×3)/10=2.9
查找到外部节点
时表示查找失败,外部节点比判定树节点个数多1个
折半查找的最坏性能与平均性能相当接近,其时间复杂度是O(log n)
图例查找失败时的ASL=1/10(36+4*4)=17/5
度(Degree):一个顶点的度是指与该顶点相关联的边的条数,顶点v的度记作d(v)。
入度(In-degree)和出度(Out-degree):对于有向图来说,一个顶点的度可细分为入度和出度。一个顶点的入度是指与其关联的各边之中,以其为终点的边数;出度则是相对的概念,指以该顶点为起点的边数。
有 $n$ 个顶点的强连通图 最多有 $n(n-1)$ 条边,最少有 $n$ 条边
有向图:
强连通性
具有7个顶点的有向图至少应有多少条边才可能成为一个强连通图? 7 key: n
问题同:已知一个有向图具有7个顶点,且是一个强联通图,问至少多少条弧? 7 key: n
具有7个顶点的有向图至少应有多少条边一定成为一个强连通图? 37 key: (n-1)(n-1) + 1
已知一个有向图具有7个顶点,且是一个强联通图,问至多 多少条弧? 42 key: (n-1)*n
无向图:
连通性
具有7个顶点的无向图至少应有多少条边才可能成为一个连通图? 6 key:
n-1
问题同:已知一个无向图具有7个顶点,且是一个连通图,问至少多少条边? 6 key: n-1
具有7个顶点的无向图至少应有多少条边一定成为一个连通图? 16 key:(n-1)(n-2)/2 + 1
已知一个有向图具有7个顶点,且是一个强联通图,问至多 多少条弧? 21 key: *`(n-1)n /2`**
如果排序结束后,a[0]可以保证一定在a[3]前头,也就是他们原有的顺序不变,那这种排序算法就是稳定的。(比如常见的冒泡排序、基数排序、插入排序、归并排序、桶排序、二叉树排序等都是稳定的排序算法) 反之,如果不能保证原有顺序,这种算法就是不稳定的
基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序 是 稳定的排序算法
快速排序、希尔排序、堆排序、直接选择排序 是 不稳定
的排序算法。
DFS 类似 树的 先序遍历
第1步:访问A。 第2步:访问(A的邻接点)C。 在第1步访问A之后,接下来应该访问的是A的邻接点,即"C,D,F"中的一个。但在本文的实现中,顶点ABCDEFG是按照顺序存储,C在"D和F"的前面,因此,先访问C。 第3步:访问(C的邻接点)B。 在第2步访问C之后,接下来应该访问C的邻接点,即"B和D"中一个(A已经被访问过,就不算在内)。而由于B在D之前,先访问B。 第4步:访问(C的邻接点)D。 在第3步访问了C的邻接点B之后,B没有未被访问的邻接点;因此,返回到访问C的另一个邻接点D。 第5步:访问(A的邻接点)F。 前面已经访问了A,并且访问完了"A的邻接点B的所有邻接点(包括递归的邻接点在内)";因此,此时返回到访问A的另一个邻接点F。 第6步:访问(F的邻接点)G。 第7步:访问(G的邻接点)E。
因此访问顺序是:A -> C -> B -> D -> F -> G -> E
第1步:访问A。 第2步:访问B。 在访问了A之后,接下来应该访问的是A的出边的另一个顶点,即顶点B。 第3步:访问C。 在访问了B之后,接下来应该访问的是B的出边的另一个顶点,即顶点C,E,F。在本文实现的图中,顶点ABCDEFG按照顺序存储,因此先访问C。 第4步:访问E。 接下来访问C的出边的另一个顶点,即顶点E。 第5步:访问D。 接下来访问E的出边的另一个顶点,即顶点B,D。顶点B已经被访问过,因此访问顶点D。 第6步:访问F。 接下应该回溯"访问A的出边的另一个顶点F"。 第7步:访问G。
因此访问顺序是:A -> B -> C -> E -> D -> F -> G
第1步:访问A。 第2步:依次访问C,D,F。 在访问了A之后,接下来访问A的邻接点。前面已经说过,在本文实现中,顶点ABCDEFG按照顺序存储的,C在"D和F"的前面,因此,先访问C。再访问完C之后,再依次访问D,F。 第3步:依次访问B,G。 在第2步访问完C,D,F之后,再依次访问它们的邻接点。首先访问C的邻接点B,再访问F的邻接点G。 第4步:访问E。 在第3步访问完B,G之后,再依次访问它们的邻接点。只有G有邻接点E,因此访问G的邻接点E。
因此访问顺序是:A -> C -> D -> F -> B -> G -> E
第1步:访问A。 第2步:访问B。 第3步:依次访问C,E,F。 在访问了B之后,接下来访问B的出边的另一个顶点,即C,E,F。前面已经说过,在本文实现中,顶点ABCDEFG按照顺序存储的,因此会先访问C,再依次访问E,F。 第4步:依次访问D,G。 在访问完C,E,F之后,再依次访问它们的出边的另一个顶点。还是按照C,E,F的顺序访问,C的已经全部访问过了,那么就只剩下E,F;先访问E的邻接点D,再访问F的邻接点G。
因此访问顺序是:A -> B -> C -> E -> F -> D -> G