算法导论【复习】

这篇博客是作者对《算法导论》的复习整理,涵盖了算法复杂度的O(), θ(), Ω()三个渐进边界,介绍了如何通过代入法、递归树法和主方法求解复杂度。此外,内容包括排序算法(如快速排序、堆排序等)、中位数和顺序统计量的计算,以及查找算法(如二分查找和哈希查找)。博客还涉及到了分治策略、动态规划和贪心算法,并讨论了树(如二叉搜索树、红黑树、AVL树)和图(如最小生成树、单源最短路径)的相关算法。" 88142136,8209328,安卓开发入门详解:Android Studio基础操作,"['Android开发', '入门指南', 'activity', 'intent']

算法导论【复习】

教材:算法导论第三版

本博客内容均为自行整理,以供学过算法的同学快速回忆之用。

本人第一篇博客,当然要献给算法啦!博客是在复习的时候整理的,有点类似于记忆碎片,比较简略,欢迎大家在评论区讨论、补充、纠正。

算法导论期末复习大纲

【算法复杂度】:

假设所需计算机资源的量只和问题规模算法输入算法本身的函数相关。

时间复杂度:

用基本操作步数衡量,独立于机器。
在不同情况下算法复杂度也是不同的,常见的情况有最好情况下,平均情况下,最坏情况下的时间复杂度。

首先,是时间复杂度的三个概念O(), θ \theta θ(), ω \omega ω()。

【算法导论】P26,三张图有助于理解

- O O O() 渐进上界

{f(n):存在正常量c, n 0 n_0 n0,使得对所有n>= n 0 n_0 n0,有0<=f(n)<=cg(n)}

- θ \theta θ() 渐进紧确界

{f(n):存在正常量 c 1 c_1 c1 c 2 c_2 c2 n 0 n_0 n0,使得对所有n>= n 0 n_0 n0,有0<= c 1 c_1 c1g(n)<=f(n)<= c 2 c_2 c2g(n)}

- Ω \Omega Ω() 渐进下界

{f(n):存在正常量c, n 0 n_0 n0,使得对所有n>= n 0 n_0 n0,有0<=cg(n)<=f(n)}

另外:

1. ο \omicron ο()代表非渐进紧确上界, ω \omega ω()代表非渐进紧确下界。o(g(n)) = {f(n): 对任意正常数c>0,使得对所有n>= n 0 n_0 n0,有0<=f(n)<cg(n)}

2. O ( ) O() O() Ω \Omega Ω()如果非常紧的话都会趋向于 θ \theta θ()。(有一种紧确界的证明方式就是这样的)

3. O ( ) O() O()往往差了些也是对的,但是不准确,比如用O( n 100 n^{100} n100)来描述快速排序的平均时间复杂度也是对的,但是不如O(nlogn)来得确切。所以比较紧的界才有意义,能求出 θ \theta θ()最好啦。(另外,用 o ( n l o g n ) o(nlogn) o(nlogn)来描述快速排序的平均情况就不行,因为 ο \omicron ο()代表非渐进紧确上界,看那个定义中是没有等号的,要注意大O和小o的区别)。

根据递归式求 O O O()和 Ω \Omega Ω()的三种方法:
  • 代入法P48:猜测复杂度式子,假设出来(可能需要带低阶的参数),代入递归式并证明。
  • 递归树法P50:把递归树画出来,然后累加(可能会用到级数相关知识)。那种分成两颗不均匀子树的情况常用这种方法求解。
  • 主方法P53:可以用递归树法推导,推导过程中会用到 n l o g b a = a l o g b n n^{log_ba} = a^{log_bn} nlogba=alogbn的性质P56。

令a$\ge 1 和 b > 1 是 常 数 , f ( n ) 是 一 个 函 数 , T ( n ) 是 定 义 在 非 负 整 数 上 的 递 归 式 : T ( n ) = a ∗ T ( n / b ) + f ( n ) , 其 中 我 们 将 n / b 解 释 为 1和b>1是常数,f(n)是一个函数,T(n)是定义在非负整数上的递归式: T(n) = a * T(n/b) + f(n),其中我们将n/b解释为 1b>1f(n)T(n)T(n)=aT(n/b)+f(n)n/b\lfloor n/b\rfloor 或 者 或者 \lceil n/b\rceil$。那么T(n)有如下渐进界。

    1. 对某个常数 ϵ > 0 \epsilon>0 ϵ>0,有 f(n) = O ( n l o g b a − ϵ ) O(n^{log_ba−\epsilon}) O(nlogbaϵ), 则 T(n) = θ ( n l o g b a ) \theta(n^{log_ba}) θ(nlogba)。(叶子节点占主导)
    1. f(n) = θ ( n l o g b a ) \theta(n^{log_ba}) θ(nlogba), 则 T(n) = θ ( n l o g b a l o g n ) \theta(n^{log_ba}logn) θ(nlogbalogn) 。(根节点叶子结点差不多)
    1. 对某个常数 ϵ > 0 \epsilon>0 ϵ>0,有 f(n) = Ω ( n l o g b a − ϵ ) \Omega(n^{log_ba−\epsilon}) Ω(nlogbaϵ), 且对某个常数c<1和所有足够大的n有 a f ( n / b ) ≤ c f ( n ) af(n/b)\le cf(n) af(n/b)cf(n),则 T(n) = θ ( f ( n ) ) \theta(f(n)) θ(f(n))。(根节点占主导)
strassen算法

书中有穿插着讲矩阵乘法的strassen算法,以它的复杂度为例。P44有伪代码。一般针对方阵,不是方阵可以补成方阵。

为什么要strassen算法?可能是因为原来效率太低了 O ( n 3 ) O(n^3) O(n3),即使做分块运算效率也很低,用加入一些矩阵加法的运算,减少矩阵乘法的运算的方法来提高效率,用strassen做矩阵乘法可以达到 O ( n l o g 2 7 ) O(n^{log_27}) O(nlog27)相当于 O ( n 2.81 ) O(n^{2.81}) O(n2.81)。strassen算法就是运用了分而治之的思想将矩阵分块套公式运算,本质上还是“简化”的矩阵乘法。

为什么strassen算法的时间复杂度是线性的?P124(1/5+7/10<1)继续迭代下去就是线性的。

博主用c语言实现了一下strassen算法,按照伪代码的思路没问题,就定义一下矩阵的加法、减法和乘法运算就可以了,在2^11次规模下真的很慢,要运行将近一个小时,比普通乘法还慢(即使用strassen算法,在较小规模的情况下可以还是使用普通乘法)。有同学用c++里的vector来实现,好像速度还挺快,以后可以尝试一下。

另外:做课后习题的时候,需要掌握简单的级数展开的知识,可以看一下前几章的内容。
lg(n!)= θ \theta θ(nlgn)课后习题会用到。

有的时候还要用到高中求解数列通项公式的技巧,同除啊换元啊什么的。

说实话,算法里有很多内容在离散数学中都讲过,感觉又温习了一遍!

P54页上有提到“非多项式意义上大于”的问题( T(n)=2T(n/2)+nlogn,nlogn并不在多项式意义上大于n),不能用主方法,但可以用递归树方法求解。具体的话,看书啦。

空间复杂度:

在此不加以赘述。

高级分析技巧【摊还分析】:

  • Ask:三种摊还分析的方法具体指的是什么?
  • Ans:聚合分析、核算法、势能法。
聚合分析:

先算出一些操作的总数上限,取平均值。(有很多操作,打包计算)

核算法:

对某些操作多分配一些代价,下次可以从信用里面取代价。(有几种操作,操作复杂度差不多,让简单操作多算几个,预支一些复杂操作的信用,简化分析过程)

势能法:

定义一个势能函数。

以书中push、pop、multipop为例,他是以栈中还有多少个元素为势函数。 c i c_i ci c i h a t c_i^{hat} cihat一个表示真实开销一个表示上界。

动态表:

Ask:动态表有什么用?

Ans:对到底需要分配多少空间,心里没有数,动态表不够用了可以扩张,存的内容变少了也可以缩减表空间。

课后习题:第一题,pultipush()不是O(1),因为不知道push多少个,如果改成multipush_5()的摊还分析,还是O(1)的级别。

n个操作合起来的代价,和一个操作的代价题目要看清楚。

找中转站的思维:分两步解决问题,1->2,2->3,则1->3,例如所有线路都通过人民广场,那么所有站点都相互可达。(所有问题都可以转换为一个问题,那个问题又有某种性质,那么所有问题都具有这种性质)。

算法分析:概率分析和随机算法

经典例题:
  • 生日问题:用逆向思维。课后例题经典,概率论中也会学习。P73。练习5.4-1
  • 球与箱子
  • 特征序列

【排序算法】:

  • 基于比较的排序算法平均时间复杂度最小为O(nlgn),基于非比较的排序平均时间复杂度最小为O(n).

基于比较的排序:

1. 插入排序 Insertion sort

给定一个数组,每次把新来的数插入之前已经排好的序列中,从只有一个数的序列直到把所有数都排列进去。例如,洗牌。

  • 时间复杂度 O ( n 2 ) O(n^2) O(n2)
2. 选择排序

头部已排好序,尾部未排好序,每次从未排好序的里面选最值放到已排好序的序列尾端。

  • 时间复杂度 O ( n 2 ) O(n^2) O(n2)
3. 冒泡排序

两重循环,两两比较,每次最值沉底或上浮。

  • 时间复杂度 O ( n 2 ) O(n^2) O(n2)
4. 归并排序

留到分治策略那一部分详细叙述。

分解:n->n/2

解决:使用归并排序递归地排序两个子序列

合并:合并两个排好序的子序列

5. 快速排序 (重点)

跟基准元比较,分成前后两堆,比基准元小的和比基准元大的。

老师在答疑课上问到我了,我应该这辈子都不会忘了。
我被问到的问题→存不存在一种算法的平均情况下的时间复杂度和最坏情况下的时间复杂度不一样?举个例子。 我回答了快速排序。

  • 平均情况下的时间复杂度:O( n l g n nlgn nlgn)
  • 最坏情况下的时间复杂度:O( n 2 n^2 n2)。如何达到? 基准元选的不好,每次都是最大值或者是最小值,每次两边分布一边很多一边很少。每次从小到大或者是从大到小排的数组,效果比较差。每次首尾和基准元比大小,不对就交换
  • 随机选取基准元的快速排序。随机选取基准元平均情况O( n l g n nlgn nlgn),最差情况也是O( n 2 n^2 n2)。

为了防止有人蓄意构造一个序列,使得快速排序变成最坏情况,我们可以随机选取基准元,**random_select()**算法。

6. 堆排序

建堆的复杂度:O(n)

堆一般用数组存储

大顶堆:根节点的值比叶子节点的值大

注意几个函数即可,建堆的时候从1/2处(第一个有孩子的结点)往上调整,和孩子比较,max-heapify()

堆排序就是构造完大顶堆之后,extract-max()和数组最后一个交换,heapsize- -,然后再对根max-heapify(),以此类推。

基于非比较的排序:

Ask:为什么非比较排序的复杂度可以到达O(n)是不是很神奇?有什么需要满足的条件吗? - 排序算法为什么可以到O(n),这不是很奇怪吗?

Ans:非比较排序的【前提】:输入数据的种类总是有限的、有一定范围的,列举一下算法的例子,例如人的身高统计,统计到厘米的精度,还是0.0000001的精度,范围很大就不一定行了。基于非比较的排序实际上是把问题范围从大问题缩小到小问题,用空间换时间。

Ask:基于比较的排序算法有一棵树,长什么样子?P107

Ans:路径的长度就是比较的次数,每个叶子节点就是输入的排序。排列组合,三个数有多少种排列,3!。多少种排序情况,就有多少个叶子结点,要经过多少次查找才能找到。

7.计数排序

有一定条件,元素个数和种类是指定的。基于统计的排序。

确定数字种类,分别统计数量,在新的数组的相应位置插入这个数字。

8.基数排序

把数分为几个部分,分别排序。例如一个三位数,先排个位再排十位,最后排百位。

分成几个位数,有限个。先排最低位。在d和k参数。。

9.桶排序

在这里插入图片描述

P119找最小值,找最大值,要找n次。同时找最小值和最大值,实际上只用找1.5n次。方法:取两个数,其中大的之和最大值比,小的之和最小值比。

【中位数和顺序统计量】:

如何求中位数:

方法一:先排序,排序。

方法二:select算法

RANDOMIZED-SELECT

O(n)的复杂度。

用了快速排序的partition方法,分两段。

可以用于找中位数,但是扩展来说找1/4,1/3位置的数都可以。找分位数的可以用于设计等高的直方图,更方便描述数据集合的轮廓。设计等高直方图,找n次SELECT可以达到O(n),也可以直接排序有O(nlogn)的复杂度,所以O(min(nlogn,n))。有多种方法哦!不要思维定势。

SELECT()算法,期望为线性时间

五个五个分组,每组排好序,找中位数,去掉四分之三不用比较的,再继续。

复杂度是O(n)

最坏时间为线性时间,暂时不考试。

【查找算法】

顺序查找

一般基于数组,无序的数组就可。O(n)

二分查找

一般基于数组,但是要有序的数组。

插入删除查询的复杂度来分析两个算法。插入:顺序查找要O(1)插在最后一位,二分查找是有序的,平均O(n)的开销。如果删了不移动,分别是O(n)和O(logn),如果删了要移动,则都是O(n)。查找代价分别是O(n)和O(logn)。

**二叉搜索树的性能来对比,二叉搜索树不是重点,不能保证任何一个复杂度,我们寻求的终点是二叉平衡树!!!**插入、删除、修改全部可以变成O(logn)。二叉搜索树可以达到O(logn)这个是不对的。

哈希查找

直接寻址法;除留余数法;开放寻址法(线性探测,二次探查,双双重散列),再哈希法

【分治策略】

分治算法怎么理解?

举几个例子:归并排序,快速排序,strassen算法的矩阵乘法。

strassen算法O( n l o g 7 n^{log7} nlog7)=O( n 2.81 n^{2.81} n2.81)很有名

【动态规划】

和分治算法的区别就是子问题可以复用。例如,分治算法的矩阵乘法,分块计算就不相关。

经典问题:

钢条切割问题

假设0-9米的钢条情况都已经知道了,到10划分最优子结构也知道了。

矩阵链的乘法
完全背包问题(贪心算法不能达到最优):

价值、体积、背包大小。子体积的最优解全部记录下来。

最长公共子序列

用一个二维的矩阵记录,一行一行来,然后有一个表达式,就是(i,j)的值和(i-1,j)和(i,j-1)相关,如果这一位刚好相等,在两者较大的哪个基础上加1即可,不相同取其中较大的那个即可。在写算法的时候一路走,一路会记录方向,方便往回找。

【贪心算法】

每次都做了局部最优得选择,但不一定是全局最优的。

经典问题:

活动选择问题/会议调度安排问题:

选择最早开始或者最不冲突的是无法达到全局最优的,但是用最早结束的是可以达到最优的。

0-1背包问题和分数背包问题
  • 老师提问→什么问题不能用贪心算法解决?举几个例子。
  • answer:0-1背包问题,不可用贪心算法解决,要用动态规划的算法解决,但是分数背包问题可以用贪心算法解决。*分数背包问题如何解决?*每次都取性价比最高的装满书包。
赫夫曼编码

为了使文本变短。对频率进行排列。

有一个性质就是头部不冲突,怎么解释?

前缀码长得不一样,使得编码是唯一的,能识别的。

【堆】

堆排序(之前提到过)

堆的话一般是数组,堆可以转变成一棵树P84。大顶堆(父结点的值比子结点)、小顶堆。

建堆的复杂度O(n), t h e t a ( n ) theta(n) theta(n)。从第一个有子节点的往上走。

堆排序的开销是O(nlogn)。堆排序的开销不可能是O(logn)。

堆的好处是可以用来做优先级队列。

优先队列

一般用堆来实现。

按照key的大小,每次选第一大的。

斐波那契堆

除了删除和取最值操作和二叉堆的代价一样,其他操作的均摊代价可以达到O(1),非常理想。P290

【树】

  • Ask:树和图的最大区别是什么?
  • Ans:树没有环。

二叉搜索树

用堆构成的二叉树,不是二叉搜索树。二叉树是左边小右边大,堆是父节点最小。

二叉树不一定是平衡树,平衡树才是目的!!

利用二叉搜索树中序遍历输出就是已经排好序的结果。

插入:比较比它则往左边走,比它大则往右边走,直到找到合适的位置。

查找:比较比它则往左边走,比它大则往右边走,直到查找。

删除:有三种案例。

红黑树

Ask:为什么红黑树是一个平衡树?

Ans:在红黑树里根节点到叶节点的路径,最长的不会超过最短的两倍,可以看作同一个量级。为什么可以满足这个性质呢?红节点底下只能是黑结点。

什么是黑高?

从该结点到叶子结点的简单路径,包含相同数目的黑色结点

例如,一颗空的树,插入一个1,那黑高等于几?黑高等于1。因为根节点必须是黑色的(但算黑高的时候,本身不算),Nil结点也是黑色的。

每次插入新的结点都是红色的。

满足几个条件:
  • 1.每个结点要么红,要么黑

  • 2.根节点为黑色

  • 3.叶子结点都为黑色

  • 4.每个红色结点下只能接黑色结点

  • 5.每个点的黑高都相等

    最后可以达到相对平衡的程度,最长边最多是最短边的两倍,不会再长了。

红黑树的旋转:

为了维护黑高的性质。

红黑树结点插入的过程,伴随旋转变色的三种情况(若插入红节点,父亲结点也为红):

1.叔结点是红色。父结点变红色,本身和叔结点变黑色。

2.叔结点是黑色,插入的结点是左孩子,制作左旋转,变case3。

3.叔结点时黑色,插入的结点是右孩子,旋转+变色。

AVL树

AVL树可以达到的效果:最短边和最长边的差距不超过1

具体的实现再说吧,了解一下好了

Huffman 树 (贪心算法中也会提到)

  • Ask:Huffman编码是什么?如何实现?
  • Ans:以往都是用定长码标记,用Huffman 树可以生成变长的前缀码,使得短的编码不是任何长的编码的前缀(代表的key都在叶子节点上,无前缀),又使得出现频率较高的编码长度较短,达到压缩的目的。用贪心算法的思想实现,每次找权重最小的两个结点,合成一个新的权重,再不断地重复上述操作,最后生成一棵树。

【散列表/哈希表】

直接寻址法,给你一个位置,在这个位置上自己扩展。拉链法。

开放寻址法,冲突了之后。线性探查:依次线性往后找;二次探查:后续探查的时候要加一个偏移量,有一个平方;双重散列:有两个散列函数线性组合去查找。反正给了这么多空间,满了就去占别人的空间。

发生哈希碰撞时的处理方法(老师提问):

  • answer:(1)拉链法(链式存储方法)新开一个链表存储(2)开放定址法:在原数组上存储。开放定址法又有一些具体的方法。

【动态表】

整块扩大和缩小 分析复杂度

【图】

图的表示:

邻接矩阵:稠密的话可以用它。

邻接链表:稀疏的话可以用它。

广度优先搜索BFS

广度优先遍历,先找到他所有的子节点,再遍历子节点下一层。

队列

深度优先搜索DFS

深度优先遍历,先找到最深的再不断遍历过来。

Ask:旅行商问题里的近似问题的算法,用的是BFS还是DFS?

Ans:旅行商问题,找欧拉回路,找最小生成树,然后双向,再优化替换点,典型的深度遍历。

最小生成树

Ask:最小生成树的定义?

Ans:树是没有回路的,和图不同。再图中找到一棵树,总权重最低。

最小生成树有什么用?answer:举个例子,使得所有的结点之间连接成本最低。

  • Ask:这两个算法的本质是什么?有什么原则?
  • Ans:本质在于有两个不同的集合,一个是已经选取的边的集合S,一个是未经选取的边的集合E-S,每次选取的边都是连接这两个集合的最短边,它必在最小生成树里,只是选取的策略不同罢了。
Kruskal
  • 每次选取最小的边(只要不是同根,不会形成一条回路)。
  • 最坏情况下:O(mlogm)
Prim
  • 从一个结点往外扩展,每次选取S和V-S之间的最短边。
  • 最坏情况下:O( n 2 n^2 n2)

单源最短路径(不能包含负权回路)

首先定义了两个函数:(v.d代表到源点的距离,v.p代表连到该点的上一个点)P377

初始化函数:每个点的v.d=无穷和v.p=nil

松弛函数:如果v.d>u.d+w(u,v),那么v.d=u.d+w(u,v),v.p=u。

感觉松弛操作更像拧紧螺丝,把线卷起来。

Bellman-Ford(权值可以为负数)*不考
  • 看书上P379的伪代码就懂了。首先,初始化。而后,对所有边进行松弛操作,这样做V-1次(对所有边的松弛操作)。最后,再对所有边用松弛条件去判断还会不会有变化,有变化则有负环,返回false,无变化则返回true。

  • 可能这样看会觉得挺有道理的,老师课上说的总结才是真的精辟。

  • 此处,长度代表有几条边。

    最短路径长度<=k-1

    ​ 第一次可以找到长度为1的最短路径

    ​ 第二次可以找到长度为2的最短路径

    ​ …

    ​ k-1轮可以算出长度为k-1的最短路径(最长的最短路径最大为k-1)

    所以这样一来应该可以算出单源最短路径了。

    再进行一轮松弛,如果有变化的话,肯定就是有负环,找不到单元最短路径的情况啦!

    算法复杂度O(VE)。也很显然有很多不必要的操作嘛,就感觉像是穷举。

参考:blog:https://www.cnblogs.com/Jason-Damon/archive/2012/04/21/2460850.html

Dijkstra(权值不能为负数)

本质是从一个点不断扩展的过程

  • Dijkstra就像摊大饼,每次都把最小的纳入(贪心策略),然后做松弛操作。
  • 算法复杂度依赖于优先队列的实现。P386普通线性搜索,总体复杂度O( V 2 V^2 V2),用二叉堆,O(ElgV),用斐波那契堆,可以改善到O(VlgV+E)。
  • 最坏情况下:O( n 2 n^2 n2)

所有结点对的最短路径

Floyd-Warshall

最大流和最小切(割)

Ask:什么是最大流?

Ans:两个点之间的流量最大,关心能不能运到,不关心运输时间。用FF算法,用增补网络。

几个概念P419的图
  • 残存网络:有残存容量(有流量不论正反,能够为容量增加的最大值)的边组成的网络
  • 增广路径 :简单来说就是从源结点s到汇点t的一条/几条可以流通的边
  • 切割:把点集分为两部分S和T;净流量(f(S,T))算s->t减去t->s,容量(c(S,T))只算s->t的。
  • 每条边上都有最大的限流,边上标注的是流量而不是距离。
  • 最大流:从源结点s到汇点t,所有的可行路加起来,最多可以送多少。
  • P425图
  • 贪心的找到一条直接去除是不行的。P425有翻转可以循环利用的。可能把非常霸道的一条边去掉了,断了多条边的路。课上有举例子。
  • P423 最大流最小切割定理 三个等价条件
Ford-Fulkerson算法

初始化增广网络。只要有一条路,就找路段中最小的当作这条路的流量。(在图中可以反向表示,即补偿表示,表示成残存网络的形式)直到没有路可以走,总流量也就求出来了。
O(E|f*|)

Edmonds-Karp算法

用广度优先搜索算法改进Ford-Fulkerson算法。
O(V E 2 E^2 E2)

P417 26.1-1 最大流G>=G’,和G<=G‘来证明最大流的值相等。假设G’中存在包含该路径的最大流,那么把该路径替换,这条路也出现在G中,G的最大流肯定大于等于它。。。

多个s,再模拟一个总体的s。P417

广度优先用队列来做,一层一层的,先把儿子都找完再找孙子。

摊大饼:估计长度+真实长度。

【P和NP】P616

P和NP

  • p表示多项式,是Polynomial的缩写。多项式时间内可解。
  • NP表示在多项式时间内可以验证结果。np问题是验证性问题,如果在多项式时间内可验证,则就是np问题。
  • np完全问题
  • 多项式可归约,怎么表示

什么是团问题?团是一个完全图,任意两点之间有连线。

什么是顶点覆盖问题?找一个点的集合,这个点的集合是最少的,对于图中的边,至少有一个顶点在里面。对于一个只有点没有边的图,顶点覆盖问题返回什么?返回空集。图里有很多点,任意两点都有边,顶点覆盖返回什么?返回N-1(N-2就不行啦)

什么是哈密尔顿回路问题?在一个大图中,是否存在所有点的简单回路。

NP是不是P不知道,只知道NP问题难度差不多。怎么解决,我们想到一些近似算法。

NP

**若一个算法能够在多项式时间之内被验证对错,这个算法被称为NP算法。**NP问题是判定问题。

例如旅行商问题:给出一条线路,要验证是不是一条小于k的线路。验证的时候只要加和看是否小于k即可。所以旅行商问题是NP问题。

排序不是一个NP问题,排序是个动作不是一个决定/问题,问一个序列是否有序,这是个NP问题,可以在多项式时间内被验证。

所有的p问题都是np问题(在多项式时间内可解、在多项式时间内可验证),但是np问题是否为p问题,尚未被验证。

np问题有很多很多。np-complete是np问题中的一类,像TSP这种可以归约到一类的问题,目前大概有一万多种(?),都是np-complete。np-complete目前还没有被证明是不是多项式级的问题,但是可以用多项式规约为一类问题。

np-hard是对难度的衡量。

几个NP问题

TSP问题 旅行商问题 高复杂度问题
  • 假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
  • 目前最好的算法O( n 2 n n2^n n2n)
clique问题,clique完全图(团,每个点和其他边都相邻),对于一个图找出里面的最大团等于几
  • 是否在图中存在团C,使得|C|>K。其实就是找图中最大的完全图。
  • O(n 2 n 2^n 2n)

对于NP问题不知道它究竟是不是P问题(究竟是不是多项式时间内可解)现在还是不确定的。

SAT问题(satisfiability problem)布尔表达式的可满足性问题

与或非表达式。看输入一串 x i . . . . . . x n x_i......x_n xi......xn用与或非把他们连接起来。是否存在一串 x i . . . . . . x n x_i......x_n xi......xn的值使得表达式为TRUE,看是否存在表达式为True的情况,看 x i x_i xi分别要取什么值。

如果是或的关系,不用连线只要一个true就行了;相反的也不连线。最后找到一个团,就有使得整个表达式为True的值。

代入验证很方便,可以在多项式时间内验证完毕。

它是NP-complete问题。

所有问题遍历完之后有O( 2 n 2^n 2n)的复杂度。

近似算法

近似算法,就是给定一个参数 a l p h a alpha alpha,返回的解在 a l p h a alpha alpha 2 a l p h a 2alpha 2alpha之间。(?)

顶点覆盖问题

可以找到一个顶点集合,所有点覆盖在里面。

如果没有边返回空集,如果是个完全图,返回N-1个结点(N-2个结点就不行了,在两个去掉的点之间连线)。

怎么解决这个问题?有一个近似算法。每次随机取一个结点,把相关的边都去掉。这样一个集合,比最优的集合,点数不超过它两倍。假设超过最优的集合的两倍了,有一个点就连不上了。(一条边连接两个点)(要涵盖一条边,两个顶点至少一个要在里面。如果都没被选中就不能被覆盖了)

小于等于最优解的两倍。

旅行商问题

做一个最小生成树,把两点之间的边double一下形成一条回路,然后它小于等于两倍的欧拉回路。欧拉回路,七桥问题,走一圈经过所有边。P655 再优化一下,经过两次的直接用一条边连接(三角不等式)。

目前最好的复杂度O( n 2 2 n n^22^n n22n)。

集合覆盖问题

用最少的人,达到最好的效果,涵盖最多的功能。贪心算法,每次找最全面的人(覆盖范围最大的),把能力范围缩小,再找最全面的人,以此类推。

归约关系

已知pi问题想知道pi’问题有多难。对于输入x’经过一个函数f,放到算法pi中求解,看输出结果。若f复杂度知道,pi的复杂度又知道,那么总体的复杂度都知道了。其实就是转化。(叫多项式规约是因为f的复杂度是一个多项式)

P638几个问题的归约的关系。

  • SAT<=Clique。SAT可以归约到Clique问题。PPT上有。 x i x_i xi 非 x i 非x_i xi两个矛盾,无连线。必选,转换成找环的问题。

对于NP问题,我们一般设计近似算法把问题解决掉。

在这里插入图片描述

实验总结

1.merge sort算法和insertion sort和bubble sort

2.Strassen算法

3.堆实现优先级队列

4.计数排序(count sort)

5.随机选择算法(rand select)、选择算法(SELECT)

6.红黑树构建

7.最长公共子序列

8.动态规划 优化的二叉搜索树

9.Dynamic table

10.贪心算法

11.最小生成树

12.dijkstra

13.最大团

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值