当前位置:   article > 正文

数据结构与算法总结(八股文)_数据结构八股文

数据结构八股文

本文适合对数据结构与算法有一定基础的进行阅读。
基于《数据结构与算法:python语言实现》的概念与算法的简要总结。

数据结构是组织和访问数据的一种系统化方式,算法是在有限的时间里一步步执行某些任务的程序。

算法分析

(1)算法分析关注的是什么?

在算法分析中,重点研究的是运行时间的增长率,采用宏观方法把运行时间视为输入大小为 n n n 的函数。

(2) O ( n ) 与 O(n)与 O(n) Ω ( n ) \Omega(n) Ω(n)的含义

用来表示算法时间复杂度的 O ( g ( n ) ) O(g(n)) O(g(n)) 的含义为算法的时间复杂度小于或等于 c g ( n ) cg(n) cg(n)
Ω ( g ( n ) ) \Omega(g(n)) Ω(g(n)) O ( g ( n ) ) 相 反 O(g(n))相反 O(g(n)),其表示的含义为算法的时间复杂度大于或等于 c g ( n ) cg(n) cg(n)

(3)简单的证明技术

反证法,归纳法,循环不变量

1.反证法

通过使用逆否命题矛盾证明
德摩根定律:“p或者q”的否定形式为"非p并非q";“p并q”的否定形式为“非p或者非q”。

2.归纳法

通过建立递推假说,实现对问题的归纳证明。

3.循环不变量

在这里插入图片描述

(4)不同时间复杂度的增长比较

在这里插入图片描述

递归

(1)递归三要素

一定有一种可以退出程序的情况;
总是在尝试将一个问题化简到更小的规模
父问题与子问题不能有重叠的部分

1.明确函数的功能
2.递归结束条件
3.递推关系式

(2)二路递归与多重递归

当一个函数执行两个递归调用时,则称其使用了二路递归,使用多于二次递归调用的即为多重递归

(3)递归的误区

尽量避免低效率的递归,因为其容易产生巨大的递归层数,影响到算法的执行效率。
其次,二路递归与多重递归随着层数的加深将产生总数巨大的函数调用,容易迅速耗尽计算机资源。

(4)python设置递归层数

在这里插入图片描述

(5)递归方法的优缺点

能够简洁地利用重复结构呈现诸多问题,通过使算法描述以递归的方式利用重复结构,经常可以避开复杂的案例分析与嵌套循环,使得算法描述的可读性更强。
但是由于需要跟踪每个嵌套调用的状态的活动记录,因此当计算机内存成本高时,从递归算法得到非递归算法是有用的。通常可以使用堆栈结构使递归算法转换为非递归算法。

基于数组的序列

(1)序列类

包括列表、元组、字符串,共性是支持下标访问。

(2)连续数组与引用数组

连续数组在内存中占用连续的内存空间(当数组中每个元素占用字节数不同时,需要按最大元素占用字节数分配空间,造成空间浪费)。
引用数组在一个数组中存储对象的引用(这样数组中每个元素占用字节数都是相同的,但是由于存储了对象的引用,也导致了占用内存增多)。
紧凑数组在内存上连续存储,原始数据按位存放。

(3)动态数组

根据添加数组元素需要,增加数组分配的内存的数目,通常预分配与当前数组元素两倍的内存,通过摊销可以达到近似O(1)的效率。

(4)列表的性能

在这里插入图片描述

在这里插入图片描述

(5)数组列表的优缺点

**优点:**可以通过索引在O(1)时间内获取数组元素。
缺点:
1.一个动态数组的长度可能超过其实际存储的数组元素所需的长度
2.在实时系统中对操作的摊销边界是不可接受的
3.在一个数组内部执行插入与删除操作的代价太高

栈、队列和双端队列

(1)HTML的标签

在这里插入图片描述

(2) 栈的性能

在这里插入图片描述

(3) 队列的性能

在这里插入图片描述

链表

(1)链表的性能

在这里插入图片描述

(2)双向链表

双向链表维护prev与next两个引用。
为了避免涉及双向链表的边界问题,通常设置头哨兵与尾哨兵。可以简化操作逻辑。

(3)设置节点的域为None

有利于实行垃圾回收,释放占用内存。

(4)启发式动态调整列表

由于访问的局部性原理,使用启发式算法,利用访问序列的局部性,每次访问一个元素即将它移动到列表的最前端。

(5)基于链表的序列与基于数组的序列

1.基于数组序列的优点:

1.数组提供复杂度为O(1)的基于整数索引的访问一个元素的方法
2.通常,具有等效边界的操作使用基于数据结构运行一个常数因子比基于链表的结构运行更有效率。
例如:队列的enqueue操作,忽略调整数组大小的问题。列表需要的操作为:一个新索引的计算,一个整数的增量,数组中存储一个元素的引用。链表需要:节点的实例化,节点的合适链接和整数增量。链表版本中CPU操作的实际数量更多,特别是考虑到新节点的实例化。
3.相较于链式结构,基于数组的表示使用存储的比例更少
在这里插入图片描述

2.基于链表序列的优点:

1.基于链表的结构为它们的操作提供最坏情况的时间界限。这与动态数组的扩张与收缩相关联的摊销边界相对应。
在这里插入图片描述
2.基于链表的结构支持在任意位置进行时间复杂度为O(1)的插入与删除。

非线性数据结构——树,树是存储一系列元素的有限节点的集合。

(1)树的结构

1.根

最顶部的元素为树根。

2.外部节点与内部节点

一个没有孩子的节点 v 称为外部节点(叶子节点),一个有一个或者多个孩子的节点 v 称为内部节点。

3.路径与边

树T的一条边指的是一对节点(v,u),u 是 v 的父节点或者 v 是 u 的父节点。路径是指一系列的节点。

(2)树的深度与高度

1.深度

假设p是树的一个节点,那么p的深度就是节点p的祖先的个数,不包括p。

2.高度

一棵非空树T的高度等于其所有叶子节点深度的最大值。

(3)完全二叉树(满二叉树)

若每个节点都有零个或者两个子节点,则这样的二叉树为完全二叉树(满二叉树)。

(4)链式二叉树性能

在这里插入图片描述

(5)树的遍历算法

1.先序遍历

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.后序遍历

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.广度优先遍历

在这里插入图片描述
在这里插入图片描述

4.二叉树的中序遍历

在这里插入图片描述
在这里插入图片描述

5.二叉搜索树

中序遍历的主要应用的把有序序列元素存储在二叉树中,定义这种树为二叉搜索树。
在这里插入图片描述

优先级队列

使用堆实现优先级队列效率更高。
在这里插入图片描述
在这里插入图片描述

排序算法

(1)插入排序

在这里插入图片描述
在这里插入图片描述

每次在插入优先级队列时选择最小值插入,效率为O(n^2)。
在这里插入图片描述

(2) 选择排序

每次从优先级队列弹出时遍历一遍,选择最小值弹出实现排序,效率为O(n^2)。
在这里插入图片描述

(3)堆排序

在这里插入图片描述

堆的原地排序示意图:
从二叉搜索树到有序列表
在这里插入图片描述

(4)归并排序

归并排序与快速排序都使用了分治法的设计模式。

1.分治法

分治法设计的三个步骤:
在这里插入图片描述

2.归并排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.归并排序性能

在这里插入图片描述

(5)快速排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

快速排序的性能

由于快速排序如果每次取最后一个元素作为基准值的话,当一个序列是已经排序好的,那么快速排序的效率则是O(n^2),因此可以使用随机快速排序,每次随机从序列中取值作为基准值
在这里插入图片描述
在这里插入图片描述

(6)桶排序

建立一个数组,数组中每个元素是一个序列,将需要排序的序列的元素值作为数组的索引,将元素值插入该索引指向的序列的末端。

该方法不依赖与比较,依赖于键值,因此只适用于下面描述的情况:
在这里插入图片描述

在这里插入图片描述

1.桶排序的性能

在这里插入图片描述

2.稳定排序

在这里插入图片描述

(7)基数排序

在这里插入图片描述
在这里插入图片描述

(8)排序算法的比较

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

哈希表

(1) 哈希函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(2)冲突的处理

当不同的关键字出现相同的哈希码时,会产生冲突。
**加粗样式**
处理冲突有两种方法:

1.分离链表

通过在桶数组中建立二级容器存储元组。
在这里插入图片描述

2.开放寻址

在这里插入图片描述
线性探测
在这里插入图片描述

使用线性探测将影响到获取元素、设置元素、删除元素几中操作。因为其是向后找到空桶插入的冲突元素。
例如:寻找元素时,找到一个索引如果键值不匹配,并向后知道找到空桶才停止。
例如:删除元素37,将导致15不能被找到(26之后有空桶)。
在这里插入图片描述

(3)负载因子

下面文字中 n 代表元组的数目(映射),N代表桶数组的大小。
在这里插入图片描述

在这里插入图片描述

(4)哈希表的性能

在这里插入图片描述

搜索树

在这里插入图片描述

(1)二叉树搜索

通过使用搜索树可以时线性时间的查找达到对数时间查找的性能。
在这里插入图片描述
二叉树高度为 log(n)
在这里插入图片描述

(2)二叉搜索树性能

在这里插入图片描述

(3)平衡搜索树

由于某些情况下树的高度与n成比例的不平衡树,因此需要保证树的高度与n成对数关系,以保证算法的性能。
通过旋转与重组可以实现树的平衡。

1.AVL 树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.伸展树

伸展树是向前启发式搜索树的一种变形,伸展树在伸展时仍然保持着有序树的特征。
其在最差情况下的效率为O(n)但是其摊销性能较好为O(logn)。

在这里插入图片描述
在这里插入图片描述
搜索14时的伸展操作。
在这里插入图片描述
在这里插入图片描述

3.(2,4)树

(2,4)搜索树是多路搜索树的一种特殊例子。
(2,4)搜索树的属性:
在这里插入图片描述
在这里插入图片描述
由于(2,4)树的子节点中有一个前节点 c 0 = − ∞ c_0=- \infty c0=与一个后节点 c d = + ∞ c_d = +\infty cd=+,如果一个内部节点是d-node则其有d-1个子节点有值,分别为 c 1 , . . . c d − 1 c_1,...c_d-1 c1,...cd1,其中 d 最大为4。

(2,4)树的性能与AVL树一样。
通过分裂融合操作保持平衡。

4.红黑树

在这里插入图片描述

红黑树的渐进性能与AVL树的渐进性能相同。

红黑树的优点是在于插入或删除只需要常数步的调整操作。AVL树与(2,4)树在最坏情况下需要对数倍的时间操作。

文本处理

(1)模式匹配算法

1.穷举法

在这里插入图片描述
假设进行模式匹配,模式字符串长度为 m m m,文本字符串长度为 n n n,则时间复杂度为 O ( n m ) O(nm) O(nm)

在这里插入图片描述

2.Boyer-Moore算法

在这里插入图片描述
首先,镜像启发式算法只要匹配最后一个模式字符,如果不匹配,那么前边的字符就不必再进行比较了,增加了效率。
例如:只需比较S与E,前面六个字符即不必比较了。
在这里插入图片描述
那么与此相结合的字幕跳跃式启发式算法,分两种情况,
第一种情况是:如果不匹配的字符不在模式字符串内,则直接将模式字符串跳跃到不匹配字符之后。

例如:从上图到这里
在这里插入图片描述
第二种情况是:将模式字符串中移动到包含该不匹配字符的位置。
例如:
在这里插入图片描述
在这里插入图片描述
其中查找模式字符串中是否包含不匹配字符以及所在位置可以通过简历一个查阅表以提高效率,达到 O ( 1 ) O(1) O(1)的查找效率。

该算法中还结合了KMP算法的思想,这部分放在KMP算法中介绍。

3.Knuth-Morris-Pratt 算法

KMP算法主要是通过一个失败函数(部分匹配值表)来实现提高效率的。

这个部分匹配值只需要根据模式字符串计算即可得到。
在这里插入图片描述
"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,

- "A"的前缀和后缀都为空集,共有元素的长度为0;

- "AB"的前缀为[A],后缀为[B],共有元素的长度为0;

- "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

- "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

- “ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A”,长度为1;

- “ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB”,长度为2;

- "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

由此即可计算得到部分匹配值表。

KMP算法的执行分为两种情况,第一种情况是字符不匹配,并且没有已匹配字符:
例如:
在这里插入图片描述
不匹配,后移一位
在这里插入图片描述
不匹配,后移一位
在这里插入图片描述
第二种情况是:已有匹配字符,但是模式字符串未完全匹配
例如:
在这里插入图片描述

移动位数 = 已匹配的字符数 - 对应的部分匹配值

此时移动位数(4)=已匹配字符数(6)-对应部分匹配值(2)
得到:
在这里插入图片描述
此时移动位数(2)=已匹配字符数(2)-对应部分匹配值(0)
在这里插入图片描述

(2)动态规划

在这里插入图片描述

(3)文本压缩与贪心算法

1.霍夫曼编码

在这里插入图片描述
在这里插入图片描述

2.贪心算法

在这里插入图片描述

(4)字典树

1.标准字典树

在这里插入图片描述

2.压缩字典树

在这里插入图片描述
压缩了标准字典树的链替换为单独的边。
在这里插入图片描述

3.后缀字典树

在这里插入图片描述

(5) 搜索引擎索引

在这里插入图片描述

图算法

(1)什么是图?

在这里插入图片描述

(2)端部顶点、输入边与度

在这里插入图片描述

(3)简单的图

在这里插入图片描述

(4)连通与强连通

在这里插入图片描述

(5)子图与生成子图

在这里插入图片描述

(6)森林与树

森林是没有循环的图,树是连通的森林,即没有循环的连通图。

(7) 图的数据结构

在这里插入图片描述

1.不同图的数据结构的性能:

在这里插入图片描述
在这里插入图片描述

2.边列表

在这里插入图片描述

3.邻接数据结构

在这里插入图片描述

4.邻接图结构

在这里插入图片描述

5.邻接矩阵结构

在这里插入图片描述

(8)图遍历

在这里插入图片描述

1.深度优先搜索

在这里插入图片描述
在这里插入图片描述

2.广度优先搜索

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

(9)传递闭包

在这里插入图片描述
当存在一个以集合 v 1 , . . . , v n {v_1,...,v_n} v1,...,vn中的点为中间顶点的有向路径的,其首尾端点的边在原图 G → \overrightarrow{G} G 的传递闭包 G n → \overrightarrow{G_n} Gn 中。
在这里插入图片描述
在这里插入图片描述

(10)有向非循环图

没有有向循环的图为有向非循环图。

1.拓扑排序

在这里插入图片描述
在这里插入图片描述

2.最短路径

在这里插入图片描述
加权图的最短路径是路径中每个边的权重的总和。

3.Dijkstra算法

利用了贪心设计模式,从一个顶点向它的邻域扩散,逐次更新顶点的最小路径值。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(11)最小生成树

未完待续

Python语言

(1) Python中的变量和函数前的单个下划线“_”与双下划线“__”:

Python不支持正式的访问控制,但以一个下划线开头的名字都看作受保护的,而以双下划线开头的名字(除了特殊的方法)是被看作私有的。虽然是看作被保护和私有的,但是也可以访问,只是不建议访问。

(2) 代码风格规范

在这里插入图片描述

参考资料

http://www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html
https://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html
https://book.douban.com/subject/30323938/

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/805172
推荐阅读
相关标签
  

闽ICP备14008679号