主定理:如果有一个问题规模为 n,递推的子问题数量为 a,每个子问题的规模为n/b(假设每个子问题的规模基本一样),递推以外进行的计算工作为 f(n)(比如归并排序,需要合并序列,则 f(n)就是合并序列需要的运算量),那么对于这个问题有如下递推关系式:
然后就可以套公式估算递归的时间复杂度
B树和B+树定义与区别M阶B树
定义
任意非叶子结点最多只有M个儿子,且M>2
根结点的儿子数为[2, M]
除根结点以外的非叶子结点的儿子数为[M/2, M],向上取整
非叶子结点的关键字个数=儿子数-1
所有叶子结点位于同一层
k个关键字把节点拆成k+1段,分别指向k+1个儿子,同时满足查找树的大小关系
特征
关键字集合分布在整颗树中
任何一个关键字出现且只出现在一个结点中
搜索有可能在非叶子结点结束
其搜索性能等价于在关键字全集内做一次二分查找
M阶B+树
定义
有n棵子树的非叶子结点中含有n个关键字(b树是n-1个),这些关键字不保存数据,只用来索引,所有数据都保存在叶子节点(b树是每个关键字都保存数据)
所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接
所有的非叶子结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字
通常在b+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点
同一个数字会在不同节点中重复出现,根节点的最大元素就是b+树的最大元素
特征
b+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”
b+树查询必须查找到叶子节点,b树只要匹配到即可不用管元素位置,因此b+树查找更稳定(并不慢)
对于范围查找来说,b+树只需遍历叶子节点链表即可,b树却需要重复地中序遍历
并查集用于解决
两个元素是否在同一个集合(优化,查的过程中把路过的节点直接连头节点)
合并两个元素所在的集合
实现
数组
双HashMap
红黑树特点
每个节点非红即黑
根节点总是黑色的
如果节点是红色的,则它的子节点必须是黑色的(反之不一定)
每个叶子节点都是黑色的空节点
从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)
跳跃表
特点
最底层包含所有节点的一个有序的链表
每一层都是一个有序的链表
每个节点都有两个指针,一个指向右侧节点(没有则为空),一个指向下层节点(没有则为空)
必备一个头节点指向最高层的第一个节点,通过它可以遍历整张表
前缀树/字典树(Trie)用于解决
常用于快速检索
大量字符串的排序和统计
基本性质
根节点不包含字符,除根节点外每个节点只包含一个字符
从根节点到某个节点,路径上所有的字符连接起来,就是这个节点所对应的字符串
每个节点的子节点所包含的字符都不同
如何从暴力递归改动态规范首先写好一个暴力递归
分析这个递归是否有重复计算
分析这个递归的当前状态和之前递归计算的顺序是不是无关
如果都满足就可以改成动态规划
改写成DP
找出递归中变化的参数
确定递归的开始位置
确定一些边界或者特殊情况
抽象出一次递归的步骤,分析步骤和已经固定的边界的关系
找出规律后coding
布隆过滤(Bloom Filter)解决问题:判断一个元素是否在一个集合中,优势是只需要占用很小的内存空间以及有着高效的查询效率
原理:保存了很长的二进制向量,同时结合 Hash 函数实现
首先需要k个hash函数,每个函数可以把key散列成为1个整数
初始化时,需要一个长度为n比特的数组,每个比特位初始化为0
某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1
判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。
特点
只要返回数据不存在,则肯定不存在
返回数据存在,只能是大概率存在
不能清除其中的数据
计算误差
先根据样本大小n,可以接受的误差p,计算需要申请多大内存m
再由m,n得到hash function的个数k
再计算实际的误差p
Java 核心 HashMap应用场景
键值对高效存取
数据结构
1.7数组加链表
1.8数组加链表加红黑树
线程安全
不安全
多线程下使用扩容步骤存在问题
线程安全的替代
Hashtable
ConcurrentHashMap
默认大小
初始数组默认长度16
何时扩容
HashMap.Size >= Capacity * LoadFactor当前容量超过总容量乘以散列因子(默认0.75)就扩容,每次2倍扩容
扩容机制
jdk1.7