文章目录
北邮算法课笔记
时间复杂分析
- 符号表示
- 递归时间复杂度求解
-
情况:
-
求解方法:
-
替换法(substitution method )
- 定义:即在归纳假设时,用所猜测的值去代替函数的解。
- 用途:确定一个递归式的上界或下界。
- 缺点:只能用于解的形式很容易猜的情形。
- 总结:这种方法需要经验的积累,可以通过转换为先前见过的类似递归式来求解。
- 解法
- 猜测解的结果
- 使用数学归纳法验证猜测结果
- 示例:
T ( n ) = 2 T ( n / 2 ) + O ( n ) → T ( n ) = O ( n l g n ) T(n) = 2T(n/2) + O(n) \rightarrow T(n) =O(n lg n) T(n)=2T(n/2)+O(n)→T(n)=O(nlgn)
-
迭代方法(iteration method)
-
迭代法的基本步骤是迭代地展开递归方程的右端,使之成为一个非递归的和式,然后通过对和式的估计来达到对方程左端即方程的解的估计。
-
示例:
- 示例1:
- 示例2:
- 示例1:
-
示例3:
-
主定理:
- 适用情况
- 定理:
- 适用情况
-
快速记忆:
与 f ( n ) f(n) f(n)相比:- 若函数 n l o g b a n^{log_ba} nlogba更大,如情况1,则 T ( n ) = Θ ( n l o g b a ) T(n) = \Theta(n^{log_ba}) T(n)=Θ(nlogba) ;
- 若两函数相等,如情况2,则 T ( n ) = Θ ( n l o g b a l o g n ) T(n) = \Theta(n^{log_ba}logn) T(n)=Θ(nlogbalogn) ;
- 若函数 n l o g b a n^{log_ba} nlogba小于 f ( n ) f(n) f(n),且满足 a f ( n / b ) < c f ( n ) af(n/b)<cf(n) af(n/b)<cf(n) ,如情况3,则 T ( n ) = Θ ( f ( n ) ) T(n) = \Theta(f(n)) T(n)=Θ(f(n)) ;
-
-
-
排序算法
-
插入排序 Insertion Sort
- 伪代码
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 伪代码
-
归并排序(Merge Sort)
-
伪代码
-
时间复杂度:
-
由递归求解或主定理得: T ( n ) = Θ ( n l o g n ) T(n) = \Theta(nlogn) T(n)=Θ(nlogn)
-
-
堆排序heap sort
-
堆的表示
- 使用List表示堆
- 使用List表示堆
-
性质:
- 由大根堆构建的树是根节点大于其所有子树的节点
-
基本算法
- Heapify
复杂度分析:
T ( n ) = Θ ( l o g n ) T(n) = \Theta(logn) T(n)=Θ(logn)
- Heapify
-
BuildHeap
时间复杂度: O ( n ) O(n) O(n),具体原因是采用了更加高效的构建方式,从低向上的递归 -
HeapSort
时间复杂度: -
优先队列
-
-
快速排序QuickSort
-
平均时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn),最坏情况的时间复杂度: O ( n 2 ) O(n^2) O(n2)
使用:替代方法(Substitute function)
-
伪代码:
-
-
决策树高度大于等于nlogn,即 h = Ω ( n l o g n ) h = \Omega(nlogn) h=Ω(nlogn)
- 任何比较排序算法在最坏情况下都要经过Ω(n lgn)次比较。
- 从根结点到任意一个可到达叶结点之间的最长简单路径的长度,表示的就是对应排序算法中最坏情况下的比较次数。因此,一个比较排序算法中的最坏情况的排序次数就等于决策树的高度。并且,当决策树中所有排列都是以可到达的叶结点的形式出现时,该决策树高度的下界也就是比较排序算法运行时间.
-
计数排序counting sort
- 此算法没有比较,所以小于 nlogn
- 时间复杂度: O ( n ) O(n) O(n)
- 伪代码:
-
基数排序Radix Sort(桶排序)
- 时间复杂度:O(n)
- 从低位向高位排序
如果要从高位排序, 那么次高位的排序会影响高位已经排好的大小关系. 在数学中, 数位越高,数位值对数的大小的影响就越大.从低位开始排序,就是对这种影响的排序. 数位按照影响力从低到高的顺序排序, 数位影响力相同则比较数位值. - 桶排序
时间复杂度O(n+C) - 基本思想:
- 每轮针对一个数位,从低位到高位,
- 每轮排序建立0-9十个桶, 数按照相对位放进相应的桶中
- 桶使用队列的方式,先进先出,得到一轮的结果
- 进行下一轮,如果高位没有,则认为为零
-
Randomed Selected
-
应用:挑选第k小个元素
-
伪代码:
-
时间复杂度分析
快速划分时间复杂度:- Assume T(n) <= cn for sufficiently large c:
-
算法的复杂度
-
动态集结构
- BST(二叉搜索树)
-
数据结构
- key:值
- left:左孩子节点(可以为NULL)
- right:右孩子节点(可以为NULL)
- p:父节点(root节点的父节点为null)
-
属性:
- 根节点的值大于其所有左子树节点的值,小于其右子树的节点的值
- 根节点的值大于其所有左子树节点的值,小于其右子树的节点的值
-
Tree Walk打印顺序列表(inorder中序遍历)
-
二叉树搜索Search
-
插入insert
- 对key值进行搜索,找到null的位置,将其插入到null位置
-
插入和搜索的时间复杂度: O ( l o g n ) O(logn) O(logn)
-
BSTSort
- 时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
- 时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
-
successor 与predecessor
- 如果存在右子树,下一个successor为右子树的最左下角的节点(右子树中最小的节点)
- 如果不存在右子树,下一个successor为父节点
- predecessor 类推
-
Delete
- 没有孩子节点:直接移除
- 只有1个孩子节点: 将孩子节点拼接到删除节点的位置
- 有2个孩子节点:将删除节点与successor交换,然后再套用前两种情况
-
- 红黑树
-
定义:
- 二叉搜索树添加了节点颜色(Red or Black)
- 目的是使高度: h = O ( l o g n ) h = O(logn) h=O(logn)
-
基本属性:
- 每个节点或者是黑色,或者是红色。
- 根节点是黑色。
- 每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
- 如果一个节点是红色的,则它的子节点必须是黑色的。
- 任意一结点到其下面的每个叶子结点的路径都包含数量相同的黑结点。
-
注意:
- 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
- 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
- 如果一个结点存在黑子结点,那么该结点肯定有两个子结点
- 定义:bh(x)代表x节点到叶子节点黑节点的数目
-
时间复杂度
- 红黑树的时间复杂度为: O ( l o g n ) O(logn) O(logn)
- 定理:一棵含有n个节点的红黑树的高度至多为2log(n+1).
- 证明:
-
旋转rotation
- 旋转的目的是让树保持红黑树的特性
- 左旋与右旋
- 与平衡树的左旋与右旋的方式相同
-
左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。如图。
-
右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。如图。
-
-
红黑树插入
插入操作包括两部分工作:一查找插入的位置;二插入后自平衡。查找插入的父结点很简单,:-
第一步: 将红黑树当作一颗二叉查找树,将节点插入。
- 若根结点为空,那么插入结点作为根结点,结束。
- 若根结点不为空,那么把根结点作为当前结点;
- 若当前结点为null,返回当前结点的父结点,结束。
- 若当前结点key等于查找key,那么该key所在结点就是插入结点,更新结点的值,结束(只更新key值(当用于treeset和hashmap时))。
- 若当前结点key大于查找key,把当前结点的左子结点设置为当前结点,重复步骤4;
- 若当前结点key小于查找key,把当前结点的右子结点设置为当前结点,重复步骤4
-
第二步:将插入的节点着色为"红色"。(这样一定不会违背红黑树的定理5)
-
-
如果父节点在祖父节点左侧,插入节点在父节点的右侧,以父节点为支撑进行左旋,在将插入节点作为当前节点,将当前的节点变为黑色,祖父节点变为红色,以祖父节点为支撑进行右旋。
-
相似:类比操作。
-
插入的伪代码:
-
delete 删除操作
-
第一步:将红黑树当作一颗二叉查找树,将节点删除。
- 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
- 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
- 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。
-
第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。
-
跳跃表(Skip List)
-
介绍
Skip List是一种随机化的数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间)。基本上,跳跃列表是对有序的链表增加上附加的前进链接,增加是以随机化的方式进行的,所以在列表中的查找可以快速的跳过部分列表(因此得名)。所有操作都以对数随机化的时间进行。Skip List可以很好解 决有序链表查找特定值的困难。 -
定义
-
Skip List构造步骤:
- 1、给定一个有序的链表。
- 2、选择连表中最大和最小的元素,然后从其他元素中按照一定算法(随机)随即选出一些元素,将这些元素组成有序链表。这个新的链表称为一层,原链表称为其下一层。
- 3、为刚选出的每个元素添加一个指针域,这个指针指向下一层中值同自己相等的元素。Top指针指向该层首元素
- 4、重复2、3步,直到不再能选择出除最大最小元素以外的元素。
-
查找操作
目的:在跳跃表中查找一个元素x
在跳跃表中查找一个元素x,按照如下几个步骤进行:- 1、从最上层的链(Sh)的开头开始
- 2、 假设当前位置为p,它向右指向的节点为q(p与q不一定相邻),且q的值为y。将y与x作比较
- (1) x=y 输出查询成功及相关信息
- (2) x>y 从p向右移动到q的位置
- (3) x<y 从p向下移动一格
- 3、 如果当前位置在最底层的链中(S0),且还要往下移动的话,则输出查询失败
-
删除操作,直接删除整个列,并删除空指针
-
课件中为双向,方便寻找predecessor
- 查找操作,每层为查找时间消耗为O(1),最多O(logn)层,所以时间复杂度为O(logn)
哈希表(HashTable)
- 背景
- 给出一个表Table(T)和记录x(包含key 和 value),并且支持Insert(T,x),Delete(T,x),Search(T,x)操作,表是无序的,且以上的操作可以在O(1)的时间内完成
- 寻址方式
- 直接寻址(Direct Address)
可以在 O(1) 时间内访问数组中的任意元素 - 问题:collision
- chaining(链表法):针对相应的value值使用链表进行存储
- 开放地址法(open addressing):
(线性探测法:若出现冲突,根据规则查找下一个位置) - 链表法复杂度分析:
- 负载因子(load factor): α = n / m \alpha = n/m α=n/m ,其中n为key的个数,m为插入槽的个数
- 查找key失败的平均时间复杂度: O ( 1 + α ) O(1+\alpha) O(1+α)
- 查找key成功的平均时间复杂度: O ( 1 + α / 2 ) = O ( 1 + α ) O(1+\alpha/2) = O(1+\alpha) O(1+α/2)=O(1+α)
- 哈希表的划分方式
-
除法哈希法(Division Method)
-
乘法哈希法(The Multiplication Method)
-
m
=
2
P
m = 2^P
m=2P,Knuth 认为A的最佳选择:
(
5
−
1
)
/
2
(\sqrt 5 -1)/2
(5−1)/2
以上两种方法在对手故意选择最坏情况的复杂度为O(n),
-
m
=
2
P
m = 2^P
m=2P,Knuth 认为A的最佳选择:
(
5
−
1
)
/
2
(\sqrt 5 -1)/2
(5−1)/2
-
全域哈希表(universal hashing)
- 全域哈希表:随机选择哈希函数,其方式与实际要存储的密钥无关
保证平均良好的性能,无论对手选择什么键 - 在向哈希表中插入元素时,如果所有的元素全部被哈希到同一个桶中,此时数据的存储实际上就是一个链表,那么平均的查找时间为 Θ(n) 。而实际上,任何一个特定的哈希函数都有可能出现这种最坏情况,唯一有效的改进方法就是随机地选择哈希函数,使之独立于要存储的元素。这种方法称作全域哈希(Universal Hashing)。
- 全域哈希的基本思想是在执行开始时,从一组哈希函数中,随机地抽取一个作为要使用的哈希函数。就像在快速排序中一样,随机化保证了没有哪一种输入会始终导致最坏情况的发生。同时,随机化也使得即使是对同一个输入,算法在每一次执行时的情况也都不一样。这样就确保了对于任何输入,算法都具有较好的平均运行情况。
- 全域哈希表:随机选择哈希函数,其方式与实际要存储的密钥无关
-
- 直接寻址(Direct Address)
动态规划Dynamic Programming
-
动态规划解决的问题有以下几个特征
- 最优子结构
最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。 - 无后效性
无后效性。即子问题的解一旦确定,就不再改变,不受在这之后、包含它的更大的问题的求解决策影响。 - 子问题重叠
子问题重叠性质。子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。
- 最优子结构
-
解决动态规划问题的一般步骤
- 寻找最优子结构(状态表示)
- 归纳状态转移方程(状态计算)
- 边界初始化
-
最长公共子序列问题(LCS)
- 最有子结构: c [ i , j ] c[i,j] c[i,j]表示在x长度来i,y长度为j的情况下的最长公共序列的长度
- 状态转移方程
- 边界初始化
c [ i , 0 ] = 0 ; c [ 0 , j ] = 0 c[i,0] = 0;c[0,j] =0 c[i,0]=0;c[0,j]=0 - 伪代码:
- 算法的时间复杂度: O ( m n ) O(mn) O(mn)
- 寻找最长公共子序列(LCS)
- 使用从后往前的算法:Backward:
- 当 c [ i ] [ j ] = c [ i − 1 ] [ j − 1 ] + 1 c[i][j] = c[i-1][j-1]+1 c[i][j]=c[i−1][j−1]+1时,记录改为对应的字符,直到i=0或j =0;
- 记忆技术:
- memoization算法将递归作为控制结构,并且是自上而下的算法,而动态编程则使用迭代作为控制结构,并且是自下而上的算法。
- 伪代码:
-
0-1背包问题(0-1 Knapsack problem)
- 最优子结构
- B [ k , w ] B[k,w] B[k,w] 在只有前k件物品时,容量w的背包的最大价值
- 状态转移方程:
- 边界条件初始化
- B [ 0 , w ] = 0 , B [ k , 0 ] = 0 B[0,w] = 0,B[k,0] =0 B[0,w]=0,B[k,0]=0
- 伪代码
- 算法时间复杂度: O ( n W ) O(nW) O(nW)
- 找出最大价值时加入哪些物品,还是使用backward算法
- 最优子结构
-
编辑距离问题(The edit distance problem)
- 最优子结构:
- D [ i , j ] D[i,j] D[i,j] 代表x字符串长度为i,y字符串长度为j时需要最短的编辑距离长度
- 状态转移方程
- D [ i , j ] = m i n ( D [ i , j − 1 ] + 1 , D [ i − 1 , j ] + 1 ) D[i,j] = min(D[i,j-1]+1,D[i-1,j]+1) D[i,j]=min(D[i,j−1]+1,D[i−1,j]+1)---------if ( t [ i ] ! = t [ j ] ) (t[i]!=t[j]) (t[i]!=t[j])
- D [ i , j ] = m i n ( D [ i − 1 , j − 1 ] , D [ i , j − 1 ] + 1 , D [ i − 1 , j ] + 1 ] D[i,j] = min(D[i-1,j-1],D[i,j-1]+1,D[i-1,j]+1] D[i,j]=min(D[i−1,j−1],D[i,j−1]+1,D[i−1,j]+1]------if ( t [ i ] = t [ j ] ) (t[i]=t[j]) (t[i]=t[j])
- 初始化边界条件
- D [ i , 0 ] = i ; D [ 0 , j ] = j D[i,0] =i;D[0,j] = j D[i,0]=i;D[0,j]=j
- 最优子结构:
-
最优二叉搜索树问题
- 最优子结构:
- m [ i , j ] m[i,j] m[i,j]从字符位置i到字符位置j的最小搜索概率
- 状态转移方程
- 时间复杂度: O ( n 3 ) O(n^3) O(n3)
- 最优子结构:
-
Viterbi algorithm维特比算法
- 使用动态规划的思想解决分层的最短路径的问题
贪心算法
-
贪婪的算法总是做出目前看起来最好的选择
-
定义
- 每次做一个贪婪的(局部最优选择)
- 自顶向下的解决方式
- 全局最优由局部最优之后得到
-
活动选择
- 查找兼容活动的最大尺寸子集
- 每次兼容的寻找结束时间最早的
- 这里的伪代码都假设已经按结束时间排好序了!!!
-
Prim 算法
- 时间复杂度:
- 时间复杂度:
-
Bellman Ford 算法
- 伪代码
- 伪代码
-
Dijkstra 算法
-
Kruskal算法
- 伪代码
- 伪代码
-
可拆分的0-1背包问题
- 求出物品的平均价值并排序
- 然后由高到低放入
Backtracking 回溯算法
- 算法1
- 伪代码
- 伪代码
- N皇后问题
- 伪代码
- 伪代码
蒙特卡罗算法( Monte Carlo)
-
内容:
- Random number generators
- Random variate sampling
- Monte Carlo integration
- Variance reduction techniques
- Qausi-Monte Carlo methods
- Some applications for stochastic simulation
-
优缺点:
- 优点:
- 易于实现
- 容易理解
- Robust鲁棒性强(当使用复杂的综合体和域)
- 高效用于高维积分
- 对于选择点是高效的解决方案
- 缺点:
- 慢
- 存在噪声
- 优点:
-
随机数生成(Random number generators)
- 线性同余发生器(Linear Congruential RNG)
- 公式:
参数:multiplier:a>1,increment(增量):integer c ( 0 < = c < m ) (0<=c<m) (0<=c<m),the modulus of the generator(生成器的模):m
随机种子: x 0 x_0 x0,
x i x_i xi位于区间: 0 < = x i < m 0<=x_i<m 0<=xi<m
x i x_i xi通过除以m被整合到区间: [ 0 , 1 ) [0,1) [0,1)
在 [ 0 , 1 ) [0,1) [0,1)的间隔内获取随机数 - MCG
- 当
c
=
0
c=0
c=0时,被称为multiplicative congruential generator (MCG)
- 当
c
=
0
c=0
c=0时,被称为multiplicative congruential generator (MCG)
- 反向同余生成器(Inversive Congruential Generators)
基于乘数反向同余生成器 - 任何随机生成数的属性:
- 数字序列必须“随机”分布:应该能够通过与一系列真正随机数字相同的随机性统计测试
- 必须有一个很长的周期
- 便携性
- 发电机的效率,即生成随机数所需的时间
- 公式:
- 线性同余发生器(Linear Congruential RNG)
-
离散概率分布
-
伯努利实验
-
泊松分布
- 泊松分布显示事件在指定时间段内可能发生的次数。它用于在给定时间间隔内以恒定速度λ发生的独立事件。
- 泊松分布显示事件在指定时间段内可能发生的次数。它用于在给定时间间隔内以恒定速度λ发生的独立事件。
-
采样连续分布
-
逆采样变换方法(Inverse-Transform Method)
- 求解过程
- 示例:
- 求解过程
-
Box and Müller Algorithm
-
接受拒绝采样(Acceptance-rejection method)
-
算法步骤
-
算法流程
-
算法理解:
-
-
圆的采样示例:
-
Monte Carlo Integration(蒙特卡罗积分)
- 基本定理:
均匀分布
非均匀分布
中心极限定理
置信区间:
- 基本定理:
-
-
蒙特卡罗积分
-
Qausi-Monte Carlo method
- 准随机数由计算机以算法生成,类似于伪随机数,同时具有根据等分序列进行确定性选择的额外重要属性,以便最大限度地减少错误。
-
蒙特卡洛积分
- $F_N $的最终表达式
- F N = 1 N ∑ 1 N f ( x ) p ( x ) F_N = \frac{1}{N}\sum_1^N\frac{f(x)}{p(x)} FN=N1∑1Np(x)f(x)
- $F_N $的最终表达式