定义一:算法是指解决问题的一种方法或者一个过程.
定义二:算法是由若干条指令组成的有穷序列,且满足下面五个特性:
确定性:组成算法的每条指令必须是明确的,没有歧义的.
有穷性:每条指令执行的次数是有穷的。算法在执行有限次后自动结束而不会出现无限循环,并且每个步骤在有限的时间内完成.
能行性:算法中的每条指令都必须是可执行的,即每一步通过执行有限次完成.
输入:有0个或者1个以上的输入
输出:至少有一个输出,否则算法没有意义.
注意:算法和程序的区别.程序可以不满足算法的有穷性条件成为计算过程,比如操作系统.
“指标”:时间复杂度和空间复杂度
从问题的规模、基本运算到算法的计算量函数三个方面进行衡量。(独立于具体计算机的客观衡量标准)
问题规模:一个或多个整数,作为输入数据量的测度
基本运算:解决给定问题时占支配地位的运算。(一般一种,偶尔>=2种)
算法的计算量函数:用问题规模的某个函数来表示算法的基本运算量,该函数又称算法的时间复杂度。
渐进时间复杂度:当问题规模趋于极限情形时的时间复杂度。分为三种:O、Ω、Θ
三种时间复杂度的严格定义:
1.T(n)=O(f(n))—给出了算法时间复杂度的上界
若存在c>0和正整数n0≥1,使得当n≥n0时,总有T(n)≤c*f(n)
2.T(n)=Ω(f(n))–给出算法时间复杂度的下界
若存在c>0和正整数n0≥1,使得当n≥n0时,存在无穷多个n ,使得T(n)≥c*f(n)成立
3.T(n)=Θ(f(n))—给出算法时间复杂度的上下界(确界)
若存在c1,c2>0和正整数n0≥1,使得当n≥n0时,总有T(n)≤c1*f(n);且存在无穷多个n,使得T(n)≥c2 *f(n)成立,即:T(n)= O(f(n))与T(n)=Ω(f(n))都成立
最坏情况时间复杂度:所有输入中基本运算执行次数为最多的时间复杂度
平均情况时间复杂度:所有输入的算法时间复杂度的平均值
五个方面:
正确性:评价算法的首要因素。
健壮性:对不正确的输入也要能够应对处理。
简单性:可读性好,易调试、改进
高效性:时间、空间复杂度较小,特别是时复
最优性:证明所给算法是解决同一类问题中最好的。
分治法就是将一个规模为n的问题分解成k个规模较小的子问题进行求解,这些子问题相互独立且与原问题相同。递归的解这个子问题,然后将各个子问题的解合并得到原问题的解。
总体而言,分治法分为3步骤:分解、求解(递归)、合并
一个问题可以通过组合非重叠子问题的最优解来解决,被称为分治法。
分解原问题的规模,为了获得较好的合并代价,需要平衡子问题的规模,但不一定都需要严格等分的划分比如像快排的划分。
平衡:使子问题规模尽量接近
在使用分治法和递归时,要尽量把问题分成规模相等,或至少是规模相近的子问题以提高算法的效率
递归与分治紧密相连;分治的计算式用递归式来表示
递归函数:函数包含对自身的调用
递归算法:算法包含对自身的调用。
递归式:(一个算法包含对自身的递归调用时,)递归算法其运行时间通常可以用递归式表示。
(网络寻找的内容:
递归是一种方法,而分治是一种思想。
三种解法:
代换法(难,并非首选)—只适用于解的形式很容易猜到的情形
步骤:1.猜测解的形式;2.数学归纳法证明它
实质就是数学归纳法:先对一个小的值进行假设,再推测更大值的正确性。
步骤:1.猜测解的形式;2.用数学归纳法求解出解中的常数,并证明解是正确的。
递归树方法(稍微有一点”不精准“,关注如何找到解的上界)—递归式表示算法时复时可用
树中每一个_节点表示一个子问题的代价,子问题对应某次递归函数调用,我们先将树中每层代价求和,得到每层的代价,然后将所有层代价求和得到所有层次的递归调用总代价。
画树时注意:先根据式子先画出递归树原始状态,然后根据递归式写出下面层的情况直到最后一层为T(1)为止。注意:递归树上层的都是常数c*式子(第一层从原式子末尾常量项找),最底层的是T(1)。
求解:1.求出树的深度,因为层数从0开始,所以计算时最后一层为深度减一!2.底层的节点个数为递归式的个数k^深度,注意换底公式AlogBC =ClogBA!【最后一层个数为Θ】3.求出总和从第一层加到最后一层(可以适当放大上界求解,注意使用等比数列求和公式!)4.2与3进行对比给出一个较大的值!
注意:求深度时,只关注每层变化量!
主定理方法
T(n)=aT(n/b)+f(n) 当a>=1,b>1且a和b均为常数时满足主定理要求 f(n)为渐进正函数
比较nlogba与f(n),只有当两者相等的时候,需要在结果上加上一个lgn,其余情况选取最大值即可。
注意:当logab b=1的时候,值为0。多项式时间!
具有以下特征的问题适用:“缩“-”相同“-“独立”-”合并“
问题的规模缩小到一定程度就可以容易地解决
问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质
问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共子问题子问题的解可以合并为原问题的解
基本思路:1.先从数列中取出一个数作为基准数;2.然后分区:将比这个数大的全放到它的__右__边,__小于或者等于__它的数全放到它的__左__边;3.再对左右区间重复第二步,直到各个区间只有一个数。
注意:(仅需分解和求解)由于子数组的排序为__原地排序__,所以解的__合并不需要进行操作__,这个数组已经排好序了!
划分细节:遇到与基数相等的元素均不需要进行交换 从后往前找比它小的,从前往后找比它大的,每次只能确定基准数值的位置,其余的再次划分两个区间排序!!
时间复杂度:最坏(划分极不平衡):_n2 平均(划分相对比较平衡): nlgn
平衡的__改进__:
快速排序的运行__时间依赖于划分是否平衡__,而平衡与否又依赖于__划分的元素__(基准元素)
快速排序为了得到较好期望性能,我们通过采用快速排序的随机化版本,将从数组中随机选出一个数值与第一个元素进行交换,然后进行基准交换排序!(再依次递归!)
基本思路:当__n=1__时,直接得出__最大值和最小值__;当__n=2__时,直接__比较找出最大最小元__;当n>2时,将数据__分为大致相等的两半__,直到情况可解时,然后__合并解求出最后解__。
以上两个问题的解法相似,均是一维的数据的分解划分。
最坏时间复杂度:3n/2-2(向上取整)—也是__下界__
问题思路:1.分解:对所有点按照X坐标__从小到大排序__,进行__分割成两个集合__ 2.不断的分割进行(递归)求解每个集合中最近点对, 先求出两个集合中最近点对的长度A,取最小的值,然后考虑第三种情况:存在跨越两个集合的最近点对,只需__判断距离分割线左右距离各为A的长度内,是否有更近的点对__。
分为三种情况:1.左边集合 2.右边集合 3.跨越左右集合
时间复杂度:nlgn
题目:找出n个元素中第k小的元素
思路:1.将输入的数组元素分为__5个一组__共n/5(向上取整)个组,然后用任意一种__排序__算法,将每组中的元素排好序,并__取出每一组中的中位数__共n/5(向上去整)个;2.然后__对__找出的这些个__中位数__再进行__递归调用select__来找到他们的__中位数x__;3.划分:按照__中位数x__对输入的数组进行__划分__,x为第k小元素,如果i=k,则返回x,如果i 注意:k=3*((n-5)/10)[向下取整] -----依次判断 题目:找出集合中出现最多的数 思路:1.首先进行__排序__,2.假设__中间的数为众数__,然后用__区间内扫描法__定位与中间数相等的数,区间标记为__[p,r]__,记录它的个数,然后通过p和r将区间分为三段,接下来对两段进行扩展。 如果某个区间所有的元素个数比中间元素的数的个数还少,那么这个区间内不可能出现众数,可以不用再找,如果找到的新的数比目前的众数大,则新的众数诞生。 题目:任意子序列中都存在只有一个元素只出现过一次! 思路:利用两个额外数组记录每个元素与它相同的元素之前出现的位置和之后紧接出现的位置。 从两端进行搜索,寻找独特元素;根据独特元素进行划分为左右子序列。重复上面两个步骤,直到剩余一个元素。 首先,与分治法类似,将问题分解为规模逐渐减小的同类型子问题。与分治法不同的是,分解的子问题很多都是重复的。 两种特性: 1.最优子结构问题—一个问题可以分解为若干个高度重复的子问题,且该问题也具有最优子结构问题 2.重叠子问题—子问题间存在重叠 大小为n的问题的最优解可以由若干个子问题的同一实例最优解导出,则该问题有最优子结构。 使用空间换取时间:存储各个子问题的最优解,进而高效的找到原问题的最优解。 动态规划递推从底向上求解,逐步扩大规模。 具体方法:递推的方式逐层计算最优值并记录必要的信息,最后根据记录信息构造最优解。 总体思想:(重叠子问题)保存已解决的子问题答案,在需要的时候适用,从而避免大量重复计算。 解题步骤:1.找出最优解的性质,并刻画出结构特征;2.递归定义最优值(写出动态规划方程);3.自底向上递推方式计算出最优值;4.根据计算最优值得到的信息,以递归方程构造一个最优解。 __动态规划与分治法的区别:__动态规划的包含时最后解包含子问题的最优解,而分治法的最后解应该是子问题间比较出来的解中选取一个最优解,是不包含子问题的最优解的! 题目:给定连续的n个可乘矩阵,要求确定计算举证连乘积的计算次序,使得乘法计算次数最少! 思路(加括号):1.分析最优解的结构:假设矩阵A[1:n]在k位置断开,A[1:n]的最优解包含这A[1:k]和A[k:n]这两个矩阵的最优解,所以存在最优子结构问题。2.给出递归式:当i=j时,m[ij]=0,当i 1.最长公共子序列问题(备忘录方法) 思路:若两个串的最后一位相同则一定包含,这时候只需计算lcs(xn-1,yn-1);c(xn-1,yn-1)+1不等则计算max{C[i,j-1],Ci-1[j]},出口为有一个串长度为0;另设一个数组,当相等时则记录一下,到最后输出; 2.最长公共子串(动态规划) 思路:公式为相等则长度+1,不等则为0,再设一个 问题:从后往前比较是否相同,不同选取费用最小的 题目:从顶层走到底层,每步走相邻的结点,则经过节点的数字之和最大是多少? 思路:取最大值max{} 思路:dp i [ j ]=dp i-1 [ j-1 ]+dp i-1 [ j+1 ] 传i次到m手里的次数 dp0[1]=1 需要处理周期问题 0->n; n+1->1 思路:将已经解决的存在数组中,然后调用递归函数fi(n-1,n-2) 思路:dp(0)=1;dp[i]:以i结尾的最长递增序列:dp[i]=max{dp[i],dp[j]+1} A[i]>A[j] 思路:第一行和第一列已经确定路径,直接计算;其余的选取最大值:dp [i] [j]=max{…+1} 思路:dp[i] [j]:第i个物品 j剩余体积 dp价值 dp[i] [j] = dp[i-1] [j] j dp[i] [j] =max{dp[i-1] [j-list[i].w]+v,dp[i-1] [j]} 和不放入该物品是同样达到该体积的最大价值进行比较 用pre和cur记录当前最长波动子序列最后两个点的值,注意将相邻重复点只剩一个。 贪心策略:分两种情况:偶数高还是矮 对于偶数高的,找奇数时应该找尽量矮的,找偶数根时应该找尽量高的,代替现有值,一旦碰到不满足的就找下一根的初始值 对于偶数矮的则反之,用单调队列实现较快! “最优化问题“ (理解)贪心算法是一种不求最优解,只希望得到较为满意解的方法,可以快速得到满意的解,省去了为找到最优解要穷尽所有可能而耗费的大量时间。 适用于求解最优化问题的算法往往包含一系列步骤,每一步都有一组选择 贪心算法总作出在当前看起来最好的选择 贪心算法并不从整体最优上加以考虑,它所作出的选择只是在某种意义上的局部最优选择 贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解 在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似 与动态规划方法相比,贪心算法更简单,更直接 问题描述:一个资源需要被N个活动占用,求出最大相容活动的子集。 思路:首先按照所有活动的结束时间进行从小到大的排序,然后依次__选择结束时间尽量早__的相容活动即可! 计算过程:x轴为时间, 画出计算过程(被选择的活动从被选择开始一直延续到结束!) 问题描述:给一个带权有向图,计算从源点到目的点的最短路径问题 思路:设置一个顶点集合S,在设置一个路径长度dist。初始时顶点集合中只有源点,然后比较从源点出发到各个点的长度,选一个最短的 加入顶点集合S中,并且更新路径长度dist,当顶点集合包含目的点时结束,最短路径即为dist。 问题描述:一个无向连通带权图,给出每条边的权。要求求出生成树上各边权的总和最小的生成树。 生成树是一个包含原来所有节点的子树 两种算法:prim算法和kruskal算法 prim算法:首先任选一个点加入顶点集S,然后依次选取从该点出发的最短路径相应的顶点加入顶点集,直到包含所有顶点结束。 kruskal算法:首先把所有顶点加入顶点集G,然后将所有边按权值从小到大进行排序,在保证不产生回路的情况下,选择该边。 2类:Las Vegas算法和Monte Carlo算法 随机算法不要求对所有可能的输入均正确计算;只要求出现错误的可能性小到可以忽略;不要求对于同一个输入,算法每次执行时给出相同的结果。 前者不一定能找到解,但解出来就一定正确,若解不出来,则需要再次调用算法进行计算,直接获得解为止;应用于不能出错的应用场景 而后者解出来的可能是错误的,只能保证解的正确性不小于p(1/2~1之间),通过算法的反复执行,使发生错误的概率小到可以忽略。k次均发生错误的概率为(1-p)k;应用于小概率出错允许且比Las Vegas节省许多时间的情况下 单双错的问题 属于Las Vegas算法,总能得到问题的一个解且必定是正确的!(当一个确定性的算法在最坏和平均情况下时间复杂度有较大差别时,可在该算法中引入随机性将其改造成Sherwood算法,以减少问题的好坏输入实例间的差别) 用于消除算法所需计算时间与输入实例间的这个联系; 当一个问题已经有一个平均情况下较好的确定性算法,但在最坏情况下效率不高,此时可以引入一个随机数发生器(服从均匀分布),将该算法改写成一个随机算法,使对任何输入实例,该算法在概率意义下均有较好的性能。 若算法无法直接适用Sherwood方法,则可以采用随机预处理方法,使输入对象服从均匀分布再用确定性算法对其进行处理,效果与使用Sherwood型算法相同。 使用的Sherwood算法! 题目:求第k小的元素 思路:随机取一个元素A[i],将其余元素与之比较,小的放入S1,等于的放入S2,大于的放入S3;若S1的个数大于k,则该元素在S1中,递归调用select(K,S1);若k小于或等于s1和s2元素之和但大于s1个数时,第k小元素就是x(整个算法的出口),否则,第k小元素在S3中,此时调用select(k-s1和s2个数,s3)。 定理:若以等概率方法在n个数中随机取数,则该算法用到的比较数的期望值不超过4n。 题目:A有一个串,B有一个串,A将x发给B,由B判断是否有X=Y? 思路:1.首先判断长度,不等直接pass;2.相等,则采取”取指纹“的方法,A对X根据一定的处理方式取一个”指纹“发给B,B也用同样的方法对Y进行取"指纹",在取K次”指纹“(每次均不同)都相同的情况下,则认为两串相等!随着取的次数k的不断增大,误判率趋于0! 注意:常用指纹:mod操作,取一个小于M的质数! 公式:误匹配率(指纹相同,但串不相同)=(使得指纹相同但串不相同的质数的个数)/小于M的质数的总个数 问题:判断一个串是否是另一个串的子串(KMP 算法比较繁琐) 思路:1.X(j)=xj…xj+m-1 [从x的第j位开始,长度与Y一样的子串] (j从1到n-m+1) ;2.不去逐一比较两个串,仅逐一比较X(j)的指纹与Y的指纹。指纹取法与上面相同 题目:当一个数的数量大于一个数组总数的1/2时,称该数组含有主元素! 思路:随机找一个数组的元素,如果该元素的个数大于1/2数组个数,则数组含有主元素!多次重复调用这个方法即可!计算时间和调用的次数相关,次数越多,准确性也就越高! 1.回溯法:基本做法:搜索,它是一种可以__避免不必要搜索__的__穷举式搜索法__;适用于求解一些__组合数较大__的问题 有些问题只需要求一个解则搜索到一个解即可结束,如果要求最优解,则需搜索整个解空间树(求解目标:满足约束条件的所有解)。 基本思想:在问题的解空间树中,按__深度优先策略__,从根节点出发搜索解空间树;1.当搜索至解空间树的__任意一点__时,先__判断该节点是否包含问题的解__,不包含__则__跳过该节点为根的子树__逐层__向其祖先节点回溯,包含__则进入该子树__继续按深度优先策略搜索。 两种策略避免无效搜索,提高回溯法的搜索效率(剪枝函数):1.用__约束函数__在扩展节点处,剪去不满足约束的子树;2.__限界函数__减去得不到最优解的子树。 步骤:1.针对所给问题,定义问题的解空间;2.确定易于搜索的解空间结构;3.深度优先搜索解空间,并在搜索过程中使用剪纸函数剪去无效搜索! 生成问题状态的说明:扩展节点:正在生产儿子节点的结点;活结点:自身已生成但其儿子还没全部生成的节点;死节点:所有儿子已经产生的节点。 2.分支限界法 分支限界法__求解目标__是找出满足约束条件的一个解或者是在满足约束条件的解中找出在某种意义下的最优解。而回溯法是找出满足约束条件的所有解。 以__广度优先__或者__以最小耗费优先方式__搜索解空间树。 基本思想:每个活结点只有一次机会成为扩展节点,一旦成为则一次性产生所有儿子节点,在这些儿子节点中舍弃不可行解或非最优解的儿子节点,其余儿子节点加入活结点表中。 两种分支限界法: 队列式分支限界法:先进先出原则选取下一个节点作为扩展节点 优先队列式分支限界法:按优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。有最大堆和最小堆两种优先选择,选取队列中的值。 解空间:__树__或者图的形式 子集树和排列树 子集树:从n个元素的集合S中找出满足某种性质的子集,相应的解空间树成为子集树。 问题规模n,2n叶子节点,2n+1-1个结点。 时间复杂度2n 排列树:确定n个元素满足某种性质的排列时,相应的解空间树成为排列树。 n n! 时复n! 1.回溯法(子集树): 先构造一个解空间树,遍历这棵树。左子节点是可行节点就进入搜索(背包容量);对于__右子树先计算上界函数__:当前价值+剩余容量可容纳的最大价值<=当前最优价值,判断是否要将其剪枝。 2.分支限界法(子集树): (队列式)只需要只用上界函数判断是否需要剪枝即可 (优先队列)首先将各个物品依其单位重量价值从大到小排列;节点的优先级为已装袋物品价值加上剩下的__最大单位重量价值的物品装满背包剩余容量__的价值和。节点是否需要剪枝看上界函数是否小于最优值。 1.回溯法(排列树): 先构造一个解空间树,遍历这棵树,限界函数:当此前的路径总和大于最优解时剪去以该子节点为根的子树。 2.分支限界法(排列树): (队列式)使用上界函数(大于当前最优解)判断是否需要剪枝即可 (优先队列式)选用极小堆(当前费用最少的) P类问题、NP类问题是”判定问题“ 回答是“Yes”或者"No"的问题 定义: P类问题是多项式时间可解的问题 NP类问题是多项式时间可验证的问题 NPC问题定义:如果一个问题属于NP问题且所有的NP问题都可以约化到它(与NP类中任何问题是一样“难“的),则称它为NPC问题。 NPHard问题:非NP问题,但所有的NP问题都可以__在多项式时间内__转化为该问题的问题。即NPHard问题满足NPC问题的第二条但不满足第一条的问题。 相互之间的关系: P类问题必是NP问题,但目前无法证明NP问题是否是P问题 故P问题不等于NP问题 NPC问题必定是NP问题,是最难的NP问题。 NPC问题也必定是NPH问题。 NPH问题不一定是NP问题 相互之间关系图: 电路可满足性问题CIRCUIT-SAT—>布尔表达式的可满足行问题SAT---->三元合取范式的可满足性问题3-CNF-SAT ---->SUBSET-SUM子集和问题 ----->CLIQUE团问题---->VERTEX-COVER顶点覆盖问题---->HAM-CYCLE哈密顿回路问题---->TSP旅行售货员问题 注意:箭头为规约 实例图: 限制法:先证明问题A是NP问题,在找一个已知的NPC问题B,证明B是A的特例(在A上加上一些限制),从而证明A是NPC问题。 迄今为止,所有的NPC问题均未能找到多项式时间的算法,故当问题规模较大时,求得最优的精确解的可能性很小。所以,往往退而去求比最优精确解稍差一点的解作为问题的近似答案 近似算法一般都比较简单,但必须关注设计算法的近似解与最优解之间的差距 近似算法处理npc问题(3种):如果问题的输入规模__较小__,则可以利用__搜索策略__在指数时间内求解问题。如果输入规模__较大__,既可以利用__随机算法__在多项式时间内“高概率”地精确求解问题,也可以考虑在多项式时间内__求得问题的一个“近似解”__。 (baidu: 1.基于线性规划的近似算法 2.基于动态规划的近似算法 3.绝对近似类 4.相对近似类 5.PTAS类和FPTAS类 6.随机近似算法 教材: 1.常数近似比; 2.多项式时间近似; 3.完全多项式时间近似: 近似算法基本思想:放弃最优解,用近似算法求出的近似最优解代替最优解! (如何衡量?) 1.近似比定义:最优化问题的最优值c*产生代价,近似算法的近似最优解所得目标函数值产生代价,则近似比为两者相除取最大值! 通常情况下,近似比为输入规模为n的函数p(n)大于等于前面的最大值。 与问题规模、不同的输入实例、近似算法本身有关。 2.时间复杂度 3.相对误差:入=|(c * -c)/c*|,相对误差界ε(n)>=前面的值,(与近似比相同,输入规模n) ε(n)<=p(n)-12.6.5众数问题(找中间的数)
2.6.5提问
2.6.5.1时间复杂度含义(如上)
2.6.5.2 什么是算法,算法的几个特性是什么?(如上)
2.6.5.3 操作系统是算法吗?(不是,无穷不满足)
2.6.5.4 算法和程序的区别(有穷性)
2.6.5.5打车软件,蕴含那些可能的算法(推荐,最近点算法)
2.6.6不无聊序列
3.动态规划
3.1动态规划方法的适用范围
3.2动态规划方法求解实例
3.2.1矩阵连乘问题
3.2.2 LCS问题
3.2.3最大字段和
3.2.4 柱子拔高问题
3.2.5 数塔问题
3.2.6 传球问题
3.2.6 斐波那契,爬楼梯
3.2.7 最长递增子序列
3.2.8 最短路径问题(滑雪)
3.2.9背包问题
3.2.4 最长波动子序列(另提)
4.贪心算法
4.1基本思想(重点)
4.2贪心算法求解实例
4.2.1活动安排问题
4.2.2单源最短路径
4.2.3最小生成树
5.随机算法
5.1 随机算法的分类(重点)
5.2 Las Vegas和Monte Carlo算法的区别(重点)
5.3 Sherwood算法
5.4随机算法求解实例(重点)
5.4.1快速排序随机化版本
5.4.2求第k小元素(Las Vegas算法)
5.4.3 Testing String Equality(Monte Carlo算法)
5.4.4 Pattern Matching(Monte Carle 算法)
5.4.5主元素问题(Monte Carle算法)
6.回溯法与分支限界法
6.1回溯法与分支限界法的基本概念(重点)
6.2回溯法求解时常见的两类解空间树(重点)
6.3求解实例(重点)
6.3.1 0-1背包
6.3.2 TSP旅行商问题
7.NP完全性
7.1判定问题
问题类型
是否能在多项式时间内求解的判定问题
是否能在多项式时间内验证的判定问题
P
是
是
NP
是 or 否
是
NP-C
未知
是
7.2P、NP、NPC、NP Hard 的定义以及相关关系
7.3NPC问题实例
7.4 证明NPC问题
8.近似算法
8.1近似算法的分类
8.2近似算法的性能(重点)