注:笔记主要讲解从0入门的算法分析,帮助读者更好理解算法的逻辑并且分析算法。
1.1 时间复杂度
定义:时间复杂度用来表示算法的表现程度,我们通过测量算法走过全程的runtime来定义时间复杂度。
表示: 其中n位输入算法的大小(input size)
分类:1.最坏情况的时间复杂度(worst-case complexity),算法的上界限(upper bound)
2.最好情况的时间复杂度(best-case complexity),算法的下界限(low bound)
3.平均时间负责度(average-case complexity)
4.均摊时间复杂度(amortized time complexity)
1.1.1 Upper Bound
对于任何单调函数f,g从正整数到无穷的区间,如果最终f可以用g表示,或者
。即存在一个正实数
,
对于
。
1.1.2 Lower Bound
对于任何单调函数f,g从正整数到无穷的区间,如果最终f可以用g表示,或者
。即存在一个正实数
,
对于
。
1.1.3 Exact Bound
对于任何单调函数f,g从正整数到无穷的区间,如果最终f可以用g表示,或者
。即存在一个正实数
,
对于
。
1.1.4 Linear time Algorithm
对于一个算法来说,如果加倍输入的大小,其时间复杂度也加倍,通常表示为
1.2 排序的下界
任何基于确定性比较的排序算法在最坏的情况下(worst-case / upper bound)都必须花费的时间来对n个元素的1个数组进行排序。
函数的层次结构:
其中:多项式函数(polynomial)比对数函数(logarithm)的增长更快,指数函数(exponential)比多项式函数增长更快。
1.3 树和图
1.3.1 图
定义:用顶点和顶点之间的桥联系的成为图。
表示:用一个pair来表示(V,E)。其中V表示一系列的顶点或节点,E表示一系列的桥用来连接节点。使用邻接矩阵表示需要分配的空间,使用邻接列表来表示需要分配
的空间。
分类:自循环(self-loop)用来表示一个桥连接同一个节点两次,多个桥表示(multi-edge)两个相同的节点之间有两个或多个桥来连接。
简单图:一个图如果是简单图(simple graph),即该图没有自循环也没有多个桥。
图之间的桥梁还分为有向图(directed)和无向图(undirected),权重图(weight)和无权重图(unweight)。
理论1:如果用G表示一个图(V,E),下列语句等效:
1.G是一个树
2.G的每两个节点被一条单独的桥连接
3.G是连接着的(connected),并且V = E+1。
4.G是无环的,并且V = E+1
5.G是无环的,如果任何两个不相邻的节点用一条桥连接,就会出现一个环。
理论2: 在一个无向简单图G = (V,E)中,最多有V(V-1)/2个桥。简单来说,用渐进表示:![E= O(V^2)](https://latex.csdn.net/eq?E%3D%20O%28V%5E2%29)
注:我们定义一个密集图,如果是稀疏图
1.3.2 图旅行
定义:图旅行意味着以系统的方式到达所有的节点。
分类:下边是两种最常用的图旅行方式:
DFS(depth-first search) 即深度优先搜索(通常以栈(stack)的方式进行运算)
BFS(breadth-first search)即广度优先搜索(通常以队列(queue)的方式进行运算)
属性:图旅行的时间复杂度为
1.遍历了所有的连接上的节点
2.通过遍历连接的节电的生成树来标记桥
1.3.3 拓扑排序
定义:假设每一个节点代表一个任务,必须有前提的完成(u,v)即任务v必须完成任务u。如果G是一个有向无环图(DAG),那么必然存在一个有效路径完成所有任务。
运算:选择一个没有入口的节点,将节点加入结果并且删除节点和与其相关的出口桥。重复上述步骤。
1.3.4 平面图(Planar Graph)
定义:一个连接图是一个平面图如果它可以被画在平面上并且每一条桥都是连续的曲线并且没有两个桥交叉。
表示:
属性:平面图除了顶点和边还有不相交的面。
理论1:欧拉公式(Euler‘s formula)如果一个连接的平面图有V个节点,E个桥,F个平面,然后可以用公式表示:![V-E+F = 2](https://latex.csdn.net/eq?V-E+F%20%3D%202)
这里我们着重介绍归纳法的证明方式(inducetion)
首先证明基础案例(Base-case ),其次提出归纳假设(Inductive Hypothesis),最后向前归纳(Inductive Step)。
理论2:在任何至少有三个节点连接的简单平面图中,
。
2.1 均摊分析(Amortized Analysis)
定义:在一系列操作中,最坏的情况并不是每一次都发生,一些操作可能会简单,一些操作可能回复杂,因此,传统的最坏情况分析只能给出不尽人意的结果,我们需要平均每一步的操作时间来定义更准确的时间负责度。
表示:AC(amrotized-cost) = ,其中
表示n个操作的总花费时间。
注:会计方法(accounting method)用来计算每一次操作的花费,有些花费多,有些花费少,我们在每一步花费的时间称为均摊花费(amortized-cost)。
3堆
堆作为重点数据结构,本章将会围绕各种堆运算进行分析。
定义:具有树结构并且树中的元素按照一定的方式排列的被称为堆。
3.1 二叉堆(Binary Heap)
定义:二叉堆事基于完全二叉树(complete binary tree)的结构(注:除了最后一层可能被从左向右填充,其他层都被填充满的树)。树的区分见文底1。
分类:分为最小二叉堆和最大二叉堆。
最小堆:每个节点大于等于其母节点
最大堆:每个节点小于等于其母节点
3.1.1 二叉堆的建立与操作:
二叉堆的组成:
我们利用列表来表示堆中的元素,即一个母节点k的左子节点为2k,右子节点为2K+1。
建堆(Build-Heap):
在这里我们讨论两种建堆算法,一种为online(数据我们提前不知道),一种为offline(我们已经掌握所有的数据)
Online:通过插入来建立堆,插入n个元素。 时间复杂度
Offline:通过堆中间的元素,向前逐渐运行swap操作。时间复杂度
插入(Insert):
1.插入一个元素到最底部
2.自下而上的重新排序堆的结构。(swap)
时间复杂度:
删除最小(DeleteMin):
1.删除堆顶的元素
2.将堆最后的元素放置堆顶然后重新进行堆排序。(swap)
时间复杂度:
堆排序(HeapSort):
1.建立一个新的结果列表
2.运行n次DeleteMIn
时间复杂度:
减值(DecreaseKey)
1.减少堆中一个子叶的值
2.重新运行swap操作
时间复杂度:
3.2 伯努利堆(Binomial Heap)
定义:是一个单独的节点,
由两个
的节点组成。
表示:
属性:1.再去掉伯努利堆的一个节点后,原来的堆将会变成
,
,
....
,
2.伯努利堆每一个树的节点数量都是由伯努利计算规定的。
3.一个伯努利堆是由多个伯努利树组成,每一个伯努利树都是一个最小堆,通常我们给予指针指向整个堆中最小的数字。
运算:1.归并(Merge)
首先合并项,然后合并两个
项,最后合并
项。合并后的伯努利堆如下图:
2.删除最小(DeleteMin)
步骤:找到含有最小指针的伯努利树,删除根并将所有的亚树提升到顶端,将相同序号的树进行合并,重新给最小值设置指针。
3.插入(Insert)
从本质上来讲,插入一个元素进入伯努利堆就是合并两个伯努利堆。
Binary | Binomial | Fibonacci | |
FindMin | |||
Insert | |||
DecreaseKey | |||
DeleteMin | |||
Merge | |||
Build | Online: Offline: |
4.贪心算法(Greedy Algorithm)
定义:1.贪心算法用来解决优化问题。2.贪心在每一步找到的是本地的最优选择。3.不会永远产生最优解法。4.早期的决定不会被撤销
属性:贪心算法的两大元素。
不保证有这样的贪心算法存在,但是一个问题可以被解决遵循下边的两个特征:
1.贪心选择性质(每一个子问题生成的全局最优解的贪心选择通常通过反证法(contradiction))
2.最优亚架构(最优亚结构的证明通常通过归纳法(induction))
4.1 最小生成树(Minimum Spanning Tree)
定义:给定一个有权重的无向图,找到一个生成树(没有环)最小化所有权重。
4.1.1 克鲁斯卡尔算法(Kruskal‘s Algorithm)(通过一次建立一个桥)
步骤:1.将所有的桥通过权重进行排序
2.循环:找到最小的权重桥,并且将相关的节点加入进去(受到环的影响)。寻找下一个桥,直到所有的节点都连接进去。
时间复杂度分析:1.将桥排序:. 2:对于每一个加入的桥进行环的检测
所以整体的时间复杂度为。
注:假设我们不通过排序,直接选择桥,其算法复杂度应该为:
4.1.2 普里姆算法(Prim‘s Algorithm)(通过一个建立一个点)
步骤:1.从一个自由点开始作为亚树C
2.扩大C通过加入最小权重的桥,该图在C中只有一个结束点
3.更新从C到邻接节点,继续生长直到C有了所有的节点。
算法复杂度:DeleteMin 进行了V次,DecreaseKey进行了E次
对于Binary Heap:. 对于Fibonacci Heap:
4.2 最短路径问题-迪杰斯特拉(Dijkstra’s Algorithm)
定义:给予一个权重为正数的图G以及原点S,找到最短路径从S到所有的其他节点。
贪心算法:将所有的节点分为两类,一类是已知的,一类是不知的。基于已知的点找到不知的点其中对应的权重桥,找到最短的桥,将其对应的点加入已知类中。
时间负责度:DeleteMin 执行了V次,每执行一次加入一个节点。DecreaseKey执行了E次,每条路径都要重新刷新。 总的时间复杂度为:
5.分治算法(Divide—Conquer Algorithm)
定义:分治算法是将一个大问题分解为多个小问题,并且逐一解决之后将结果进行合并的算法。
表示:通常D&C算法的复杂度可以使用大师理论来进行解决(Master Theort)。
假设将大问题分成了个小问题,并且每一个的输入样本变为原来的
,总的时间复杂度为:
其中代表合并子问题方法的时间复杂度(包含分开问题的步骤)。由于子问题的分解可以用树表示,树的高度为
,子叶的数量可以表示为
或者
。
5.1 大师理论(Master Theory)
定义:大师理论提供直接的方法计算时间复杂度。
对于,
并且均为常数,
为正函数,
大师理论如下
Case1: 只有叶子
如果 =
,
对于
。
Case2: 所有节点
如果 =
,
,
。
Case3: 只有中间节点
如果 =
,
对于
。
5.2 寻找最大和序列(Maximum Subquence Sum)
问题:如果存在一组序列,找出中间一组子序列使其和为所有子序列的最大值。
解决:从序列中间分为两个子序列。从中间向两边以此求和,得到两边子序列的最大值。
例如序列为:3,-4,5,-2,-2,6,-3,5,-3,2
从中间分开为3,-4,5,-2,-2,|6,-3,5,-3,2
向两边求和为:0,-3,1,-4,-2,|6,3,8,5,7
所以,从[5,-2,-2,6,-3,5] 为最大和的子序列。
文底1- 区分二叉树:
二叉树分为完全二叉树(compelte binary tree:除了最后一层可能被从左向右填充,其他层都被填充满的树),满二叉树(full binary tree:每个节点含有0或者2个子节点,没有单独的子节点出现),完美二叉树(perfect binart tree:所有的层都是满层)。