常见数据结构
- 线性
数组、链表、队列、堆栈、块状数组(数组+链表)、哈希表、双端队列、位图(bitmap)
- 树
堆(大顶堆、小顶堆)、trie树(字母树或字典树)、后缀树、二叉排序/查找树、B+/B-、AVL树、Treap、红黑树、splay树、线段树、树状数组
- 图
图
- 其他
并查集
常见算法
- 基本思想:枚举、递归、分治、模拟、贪心、动态规划、剪枝、回溯
- 图算法:深度优先遍历与广度优先遍历,最短路径、最小生成树、拓扑排序
- 字符串算法:字符串查找、hash算法、KMP算法
- 排序算法:冒泡、插入、选择、快排、归并排序、堆排序、桶排序
- 动态规划:背包问题、最长公共子序列、最优二分检索树
- 数论问题:素数问题、整数问题、进制转换、同余模运算
- 排列组合:排列与组合算法
- 其他:LCA与RMQ问题
数组、链表、块状链表
- 数组:元素在内存中紧挨着存储,因此优点是定位快(O(1)),缺点是插入删除慢(O(n))
- 链表:通过指针将不同位置的元素链接起来,优缺点与数组正好相反,优点是插入删除快(O(1)),定位慢(O(n))。
- 块状链表:从整体上看,它是一个链表,而在链表的每个节点上,以数组的形式存储一组元素。将数组和链表的优点结合起来,各种操作的时间复杂度均为O(sqrt(n))。
http://dongxicheng.org/structure/blocklink/
栈、队列
http://www.cnblogs.com/yangecnu/p/Introduction-Stack-and-Queue.html
https://www.jianshu.com/p/afbfc784238a
栈(stack)是一种动态集合,它是LIFO(last in first out后进先出)结构。
- 栈可以用数组或链表来实现。栈要记录的数据有栈顶位置和栈最大大小。栈的操作包括判空(stack_empty())、压入(push(x))、弹出(pop())。
- 栈的用途很广泛,比如编译器的词法分析器、Java虚拟机、软件中的撤销操作、浏览器中的回退操作、编译器中的函数调用实现等等
队列(queue)是一种FIFO(first in first out先进先出)结构
- 队列可以用数组或链表来实现。队列要记录的数据有队首位置head(第一个元素位置),队尾位置tail(下一个元素要插入的位置,最后一个元素的下一个位置),队列最大大小size。队列的操作包括入队(enqueue(x))、出队(dequeue()),判空(empty())、队满(full())
- 队列的应用也很广泛,最广泛的就是排队,还有我们播放器上的播放列表,数据流对象,异步的数据传输结构(文件IO,管道通讯,套接字等),还有一些解决对共享资源的冲突访问,比如打印机的打印队列,消息队列,交通状况模拟,呼叫中心用户等待的时间模拟等等。
哈希表
http://www.cnblogs.com/yangecnu/p/Introduce-Hashtable.html
- 也称散列表,是根据关键码值(key-value)直接进行访问的数据结构,也就是我们常用到的map,其最大特点就是可以快速实现查找、插入、删除(O(1))。Hash表经常被用来解决大数据问题。
- 哈希表和哈希函数的标准定义:若关键字为k,则其值存放在h(k)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系h为哈希函数,按这个思想建立的表为哈希表。
位图
http://dongxicheng.org/structure/bitmap/
- 一种非常简洁快速的数据结构,能同时使存储空间和速度最优化(而不必空间换时间)。在位图中,每个元素为“0”或“1”,表示其对应的元素不存在或存在,在索引,数据压缩等方面有广泛应用
- c++的STL中有bitmap类(std::bitset),它提供了很多方法。
- 位图可用于
- 字符串全组合枚举(对于长度为n的字符串,组合方式有2^n种),如:abcdef,可以构造一个从字符串到二进制的映射关系,通过枚举二进制来进行全排序。
- 哈米尔顿距离
- 搜索:设计搜索剪枝时,需要保存已经搜索过的历史信息,有些情况下,可以使用位图减小历史信息数据所占空间。
- 压缩:1.在2.5亿个整数中找出不重复的整数,注,内存不足以容纳2.5亿个整数? 2.腾讯面试题:给40亿个不重复的unsigned int的整数,没拍过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?
树
堆
实际上是以数组形式存储的完全二叉树,也叫优先队列,基本操作至多与树的高度成正比。
- 堆需要存储的数据有数组的大小max-size,堆元素个数size(size<max-size)。堆中元素通过坐标来确定父节点、左右子节点,具体来说:一个节点i的父节点[i / 2],一个节点i的左子节点[2 * i],一个节点i的右子节点[2 * i + 1]。
- 堆的分类:(1)最大堆:所有子节点都比其父节点值小(小于等于)的堆A[i / 2] >= A[i];(2)最小堆:满足所有节点都比其父节点值大(大于等于)的堆A[i / 2] <= A[i]。
- 堆的操作:(1)维护堆的性质(HEAPIFY):这里指维护最大堆或最小堆的性质,每次添加节点,都需调整位置,使其满足最大堆或最小堆的性质。HEAPIFY耗时O(lgn)。(2)建堆(BUILD-HEAPIFY):从第一个不满足堆性质的节点,逐一调用HEAPIFY函数,即可完成建堆。建堆的期望时间为O(n)。
- 堆的应用:(1)堆排序(2)优先队列
Trie树
Trie一词来自retrieve,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树。
Trie树可以利用字符串的公共前缀来节约存储空间。如图所示,该trie树用10个节点保存了6个字符串tea,ten,to,in,inn,int
在这个trie树中,字符串in,inn和int的公共前缀是“in”,因此可以只存储一份“in”以节省空间。当然,如果系统中存在大量字符串且基本没有公共前缀,则相应的trie树将非常消耗内存,这也是trie树的一个缺点。
Trie树的基本性质可以归纳为:
- 根节点不包括字符,除根节点以外每个节点只包含一个字符
- 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串
- 每个节点的所有子节点包含的字符串不相同
Trie树的应用
- 字符串的快速检索:字典树的查询时间复杂度是O(logL),L是字符串的长度。字典树的效率比hash表高,但字典树的空间相比hash还是比较浪费的,毕竟hash可以用bit数组。
- 字符串排序:减少没必要的公共子串
- 最长公共前缀
- 自动匹配前缀显示后缀
后缀树(Suffix tree)是一种树形数据结构,就是把一串字符的所有后缀保存并且压缩的字典树,后缀树并不是针对大量字符串的,而是针对一个或几个字符串来解决问题。
后缀树的应用
- 查找字符串o是否在字符串S中
- 指定字符串T在字符串S中的重复次数
- 字符串S中的最长重复子串
- 两个字符串S1,S2的最长公共部分
参考:http://www.cnblogs.com/maybe2030/p/4732377.html
二叉树性质
- 在非空二叉树中,第i层的节点总数不超过2^(i - 1),i >= 1;
- 深度为h的二叉树最多有2^h - 1个节点(h >= 1),最少有h个节点
- 对于任意一棵二叉树,如果其叶节点数为N0,而度数为2的节点总数为N2,则N0 = N2 + 1
- 具有n个节点的完全二叉树的深度为log2(n + 1)
满二叉树
- 定义:除最后一层无任何子节点外,每一层上的所有节点都有两个子节点。
- 性质
- 一棵树深度为h,最大层数为k,深度与最大层数相同,k = h
- 叶子数为:2^h
- 第k层的节点数是:2^(k-1)
- 总节点数:2^k - 1,且总节点数一定是奇数
完全二叉树
- 若二叉树的深度为h,除第h层外,其它各层(1~(h - 1)层)的节点数都达到最大个数,第h层所有的节点都连续集中在最左边。
- 完全二叉树是效率很高的数据结构,堆是一种完全二叉树或者近似完全二叉树,所以效率极高,像十分常用的排序算法、Dijkstra算法、Prim算法等都要用堆才能优化,二叉排序树的效率也要借助平衡性来提高,而平衡性基于完全二叉树。
二叉查找树
又称二叉排序树或二叉搜索树
性质
- 若左子树不空,则左子树上所有节点的值均小于它的根节点的值
- 若右子树不空,则右子树上所有节点的值均大于或等于它的根节点的值
- 左、右子树也分别为二叉排序树
- 没有键值相等的节点
- 对二叉查找树进行中序遍历,即可得到有序的数列
- 二叉查找树的高度决定了二叉查找树的查找效率,插入和查找的时间复杂度均为O(logn),但在最坏的情况下仍然会有O(n)的时间复杂度。
平衡二叉树
定义:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在平衡二叉搜索树中,其高度一般都良好的维持在O(log2n),大大降低了操作的时间复杂度
最小平衡二叉树的节点的公式:F(n) = F(n - 1) + F(n - 2) + 1,1是根节点,F(n - 1)是左子树的节点数目,F(n - 2)是右子树的节点数目。
AVL树
是最先发明的自平衡二叉查找树。在AVL中任何节点的两个儿子子树的高度最大差别为1,所以也被称为高度平衡树,n个节点的AVL树最大深度约1.44log2n。查找、插入和删除的时间复杂度最好情况和最坏情况都维持在O(logn)。但是频繁旋转会使插入和删除牺牲掉O(logn)左右的时间,不过相对于二叉查找树来说,时间上稳定了很多。
AVL树的自平衡操作--旋转
单旋转
双旋转
红黑树
是一种自平衡二叉查找树,又称对称二叉B树,它是复杂的,但它的操作有着良好的最坏情况运行时间,可以在O(logn)时间内做查找,插入和删除,这里的n是树中元素的数目。
红黑树还是2-3-4树的一种等同,它们的思想是一样的,只不过红黑树是2-3-4树的形式表示的。
性质
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色,除二叉查找树的一般性质外,对于任何有效的红黑树,我们增加了如下的额外要求:
- 节点是红色或黑色
- 根是黑色
- 所有叶子都是黑色
- 每个红色节点必须有两个黑色的子节点
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点
这些约束确保了红黑树的关键特性:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。该特性的目的是让这个树大致上是平衡的。因为操作如插入、删除、查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树
红黑树的自平衡操作
红黑树的只读和普通二叉查找树相同,但是,在红黑树上进行插入和删除时会导致不再符合红黑树的性质。恢复红黑树的性质需要少量(O(logn))的颜色变更(实际非常快速)和不超过三次树旋转(对于插入操作是两次)。虽然插入和删除很复杂,但操作时间仍可以保持为O(logn)
B树
B树也是一种用于查找的平衡树,但是它不是二叉树。
B树是一种树状数据结构,能够用来存储排序后的数据。这种数据结构能够让查找数据、循序存取、插入数据及删除等动作,都是在对数时间内完成。B树,概括来说是一个一般化的二叉查找树,可以拥有多于2个子节点。与自平衡二叉查找树不同,B树为系统最优化大块数据的读和写操作。B树算法减少定位记录时所经历的中间过程,从而加快存取速度。这种数据结构常被应用在数据库和文件系统中。
B树作为一种多路搜索树,有以下要求
- 定义任意非叶子节点最多只有M个儿子;且M>2
- 根节点的儿子数为[2, M]
- 除根节点以外的非叶子节点的儿子数为[M/2, M]
- 每个节点存放至少M/2 - 1(取上整)和至多M-1个关键字(至少2个关键字)
- 非叶子节点的关键字个数 = 指向儿子的指针个数 - 1
- 非叶子节点的关键字:K[1], K[2], K[3], ..., K[M - 1];且K[i]<K[i+1]
- 非叶子节点的指针:P[1],P[2],...,P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M - 1]的子树,其它P[i]指向关键字属于(K[i - 1], K[i])的子树;
- 所有叶子节点位于同一层
B+树
是B树的变体,也是一种多路搜索树:
其定义基本与B树相同,除了:
- 非叶子节点的子树指针与关键字个数相同
- 非叶子节点的子树指针P[i],指向关键字值[K[i], K[i + 1])的子树(B树是开区间)
- 为所有叶子节点增加一个链指针
- 所有关键字都在叶子节点出现
B+树的搜索与B树也基本相同,区别是B+树只有达到叶子节点才命中(B树可以在非叶子节点命中),其性能也等价于在关键字全集做一次二分查找;
B+的性质
所有关键字都出现在叶子节点的链表中(稠密索引),且链表中的关键字恰好是有序的
不可能在非叶子节点命中
非叶子节点相当于是叶子节点的索引(稀疏索引),叶子节点相当于是存储(关键字)数据的数据层
更适合文件索引系统