1. 十个数据结构
数据结构与算法之美【time.geekbang.org/column/intr…】
1.1 数组
定义:是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
特点:线性、连续的内存空间
优点:随机访问快
缺点:低效的插入和删除
1.2 链表
使用场景:LRU 缓存淘汰算法
特点:通过“指针”将一组零散的内存块串联起来使用
链表结构:单链表、双向链表、循环链表
优点:高效插入和删除
缺点:随机访问低效
1.3 栈
特点:后进先出、一种“操作受限”的线性表,只允许在一端插入和删除数据
实现方式:数组实现的栈,我们叫作顺序栈,用链表实现的栈,我们叫作链式栈
1.4 队列
特点:先进先出
与栈很似,支持的操作也很有限,最基本的操作也是两个:
入队 enqueue(),放一个数据到队列尾部
出队 dequeue(),从队列头部取一个元素
实现方式:用数组实现的队列叫作顺序队列,用链表实现的队列叫作链式队列。
1.5 散列表
定义:散列表就是利用数组下标随机访问数据的优势,演化而来。
实现方式:散列函数把元素的键值映射为下标,然后将数据存储在数组中对应下标的位置。当我们按照键值查询元素时,我们用同样的散列函数,将键值转化数组下标,从对应的数组下标的位置取数据
散列函数设计的基本要求:
- 散列函数计算得到的散列值是一个非负整数;
- 如果 key1 = key2,那 hash(key1) == hash(key2);
- 如果 key1 ≠ key2,那 hash(key1) ≠ hash(key2);但现实中存在 hash(key1) == hash(key2)
散列冲突解决
key1 ≠ key2,那 hash(key1) == hash(key2),即散列冲突,散列函数也无法避免散列冲突
解决方法:开放寻址法和链表法
1.6 二叉树
树:非线性表数据结构。需要了解根节点、叶子节点、父节点、子节点、兄弟节点,还有节点的高度、深度、层数,以及树的高度。
我们平时最常用的树就是二叉树。二叉树的每个节点最多有两个子节点,分别是左子节点和右子节点。
二叉树中,有两种比较特殊的树,分别是满二叉树和完全二叉树。
满二叉树又是完全二叉树的一种特殊情况。
实现方式:既可以用链式存储,也可以用数组顺序存储;数组顺序存储的方式比较适合完全二叉树,其他类型的二叉树用数组存储会比较浪费存储空间
遍历:前、中、后序遍历操作,表示的是节点与它的左右子树节点遍历打印的先后顺序,遍历的时间复杂度是 O(n),用递归来实现。
- 完成二叉树
定义:除了最后一层,其他层的节点个数都是满的,最后一层的节点都靠左排列
- 二叉查找树
定义:二叉查找树是二叉树中最常用的一种类型,也叫二叉搜索树。顾名思义,二叉查找树是为了实现快速查找而生的
结构:在树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值
- 平衡二叉查找树
定义:二叉树中任意一个节点的左右子树的高度相差不能大于 1
目的:解决普通二叉查找树在频繁的插入、删除等动态更新的情况下,出现时间复杂度退化的问题
- 红黑树
定义:它是一种不严格(近似平衡)的平衡二叉查找树; 红黑树中的节点,一类被标记为黑色,一类被标记为红色。除此之外,一棵红黑树还需要满足这样几个要求:
- 根节点是黑色的;
- 每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存储数据;
- 任何相邻的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的;
- 每个节点,从该节点到达其可达叶子节点的所有路径,都包含相同数目的黑色节点;
目的:它是为了解决普通二叉查找树在数据更新的过程中,复杂度退化的问题而产生的。红黑树的高度近似 log2n,所以它是近似平衡,插入、删除、查找操作的时间复杂度都是 O(logn),性能都比较稳定。对于工程应用来说,要面对各种异常情况,为了支撑这种工业级的应用,我们更倾向于这种性能稳定的平衡二叉查找树。
1.7 堆
定义:堆是一种特殊的树,其满足以下两个条件,则为一个堆
- 堆是一个完全二叉树;
- 堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值
1.8 图
存储方式:邻接表、邻接矩阵
1.9 Trie 树
2. 十大经典算法
-
- 冒泡排序
-
- 选择排序
-
- 插入排序
-
- 希尔排序
-
- 快速排序
-
- 归并排序
-
- 桶排序
-
- 堆排序
-
- 计数排序
-
- 基数排序
3. 复杂度分析
3.1 时间复杂度
大 O 时间复杂度表示法:实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度。
如何分析代码的时间复杂度呢?
- 只关注循环执行次数最多的一段代码
- 加法法则:总复杂度等于量级最大的那段代码的复杂度
- 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
时间复杂度级别可以分为:多项式量级和非多项式量级。其中,非多项式量级只有两个:O(2n) 和 O(n!),多项式量级有O(1)、O(n)、O(n^2 )、O(logn)、O(nlogn)、O(m+n)、O(m*n)
3.2 空间复杂度
渐进空间复杂度:表示算法的存储空间与数据规模间的增长关系,简称为空间复杂度。常见的空间复杂度就是 O(1)、O(n)、O(n^2 ),像 O(logn)、O(nlogn) 这样的对数阶复杂度平时都用不到。
3.3 均摊时间复杂度
找出所有的输入情况及相应的发生概率,然后再计算加权平均值,针对这种特殊的场景,我们引入了一种更加简单的分析方法:摊还分析法,通过摊还分析得到的时间复杂度我们起了一个名字,叫均摊时间复杂度。