目录
- 一、贪心法
- 二、动态规划
- 三、分治法
- 四、常用搜索算法
-
- 题型一(穷举法的概念)
- 题型二(回溯法的概念)
- [题型三(DFS/BFS 搜索)](#题型三(DFS/BFS 搜索))
- 题型四(分支限界法的概念)
- 题型五(分支限界法的应用)
- 题型六(回溯法与分支限界法对比)
- 五、算法设计范式对比
✅❌
一、贪心法
题型一(贪心法的概念)
1、贪心算法的核心特征是( )。
A、总是选择当前最优解
B、回溯尝试所有可能
C、分阶段解决子问题
D、总能找到最优解
解析:(A)
贪心算法是指不考虑整体上的综合最优决策,而在局部上以最优决策来解决问题,即每次选择的都是最优的解决方案,不考虑该决策对整体的影响。A✅
贪心算法不会涉及到回溯,选择后不会回溯再尝试所有可能。B❌
动态规划会将问题分解为每个子问题,子问题重叠不独立,对子问题保存最优解,可以说是分阶段解决子问题(按子问题的规模从小到大依次求解);而贪心算法不依赖子问题的最优解,是直接每一步选择当前局部最优。C❌
贪心算法针对某一问题,只有同时具有最优子结构性质和贪心选择性质,则可以通过使用贪心算法可以得到最优解。例如,贪心算法针对 0-1 背包只能得到次优解,需要通过动态规划才能得到最优解。D❌
题型二(贪心法的应用)
1、哈夫曼编码是一种数据压缩算法。以下关于哈夫曼编码的描述中,不正确的是( )。
A、哈夫曼编码是一种变长编码,频率高的字符使用较短的编码,频率低的字符使用较长的编码
B、在构造哈夫曼树时,频率越低的字符离根节点越近,频率越高的字符离根节点越远
C、哈夫曼编码的生成过程基于贪心算法,每次选择频率最低的两个节点进行合并
D、哈夫曼编码是一种前缀编码,任何一个字符的编码都不会是另一个字符编码的前缀,因此可以实现唯一解码
解析:(B)
哈夫曼编码中任一字符的编码都不是其它字符的编码的前缀,它是一种可变长度编码,且是无前缀编码,频率越高,编码越短。A、D✅
频率越高的字符离根节点越近,频率越低的字符离根节点越远,因为频率高的字符,会在较晚的步骤中被合并,最终处于树的较顶层。B❌
贪心算法的常见应用场景,例如,哈夫曼树中的哈夫曼编码,图的应用中求最小生成树以及求单源最短路径的相关算法等。每次选择的都是两个最小权值结点,从而构成了一个局部最优解;每次选择的情况都是最小权值结点,所以这两个结点之和一定是最优解。C✅
2、下列哪些问题不能用贪心法精确求解?
A、哈夫曼编码问题
B、0-1 背包问题
C、最小生成树问题
D、单源最短路径问题
解析:(B)
哈夫曼编码问题可以用贪心法求解。A✅
贪心算法针对某一问题,只有同时具有最优子结构性质和贪心选择性质,则可以通过使用贪心算法可以得到最优解。例如,贪心算法针对 0-1 背包只能得到次优解,需要通过动态规划才能得到最优解。B❌
最小生成树问题中可以通过 Kruskal 算法和 Prim 算法实现,可以通过贪心法实现。单源最短路径问题可以通过 Dijkstra 算法实现,也是可以通过贪心法实现。都是通过每一步选当前最优的边或顶点的贪心策略,实现全局最优解。C、D✅
3、以下哪些算法不属于贪心算法?
A、Dijkstra 算法
B、Prim 算法
C、Floyd 算法
D、Kruskal 算法
解析:(C)
贪心算法:哈夫曼编码、Prim 算法、Kruskal 算法、Dijkstra 算法 (前者用于哈夫曼树,后三者用于图)。A、B、D✅
Floyd 算法是用于图,求图中一对顶点之间的最短路径。需要动态规划实现,通过枚举中间顶点,三重循环逐步更新任意两点之间的最短路径长度,所以不属于贪心算法。C❌
二、动态规划
题型一(动态规划的概念)
1、(判断题)动态规划方法将原问题分解为一个或多个相似的子问题,因此必须使用递归实现。
解析:(×)
动态规划算法的两个基本要素是最优子结构和重叠子问题,可以使用递推(迭代)或递归两种方式实现。❌
2、(判断题)递推是一种通过已知的初始值和递推公式,逐步求解目标值的算法。
解析:(√)
动态规划中,由底向上的递推法是将问题分成相互独立、可简单直接求解的子问题,并将子问题的解按由小到大的顺序保存下来,逐步构建出问题的最优解,递推主要由循环语句实现,可以避免采用递归函数时所带来的额外开销。而由顶向下的递归法可概括为递归+可记忆,一开始将问题分成子问题,通过递归先解决子问题,可记忆指的是保存每个子问题的解,从而大幅度提升计算效率,缩短时间,不过有栈溢出风险。✅
3、(判断题)动态规划算法通常有递归实现和递推实现。但由于递归调用在运行时会由于层数过多导致程序崩溃,有些动态规划算法只能用递推实现。
解析:(√)
递归实现和递推实现有相同的渐进运行时间,但递推方法通常更快,原因是避免了递归函数调用的常数开销,从而对于大规模问题来说,是更适合的,而对于小规模问题适用于递归。✅
4、以下关于递推算法基本思想的描述,正确的是( )。
A、递推算法通过将问题分解为相互独立的子问题来解决
B、递推算法通常用于穷举所有可能的解决方案
C、递推算法从已知的基础情况出发,通过某种关系逐步推导出更大规模问题的解
D、递推算法适用于在每一步做出局部最优选择以达到全局最优
解析:(C)
将问题分解为相互独立的子问题是分治法的核心思想,不是递推算法。A❌
穷举所有可能的解决方案是暴力枚举法的特点,递推算法是通过递推公式逐步推导,不需要穷举。B❌
递推算法的核心思想就是从已知的基础情况出发,利用问题本身的递推关系,逐步推导得到更大规模问题的解。C✅
贪心算法是每一步做出局部最优选择以达到全局最优,而不是递推算法。D❌
5、以下关于动态规划算法特性的描述,正确的是( )。
A、子问题相互独立,不重叠
B、问题包含重叠子问题和最优子结构
C、只能从底至顶迭代求解
D、必须使用递归实现,不能使用迭代
解析:(B)
分治法每个子问题独立,动态规划每个子问题重叠不独立。A❌
动态规划的两个基本要素是最优子结构和重叠子问题。B✅
动态规划有两种方法实现,递归是自顶向下(递归+可记忆),递推(迭代)是自底向上。C、D❌
6、以下关于动态规划的说法中,错误的是( )。
A、动态规划方法通常能够列出递推公式
B、动态规划方法的时间复杂度通常为状态的个数
C、动态规划方法有递推和递归两种实现形式
D、对很多问题,递推实现和递归实现动态规划方法的时间复杂度相当
解析:(B)
递推公式就是最优值的递归表达式,都是动态规划的核心,即状态转移方程。A✅
动态规划中斐波那契数列的状态个数为O(n),时间复杂度为O(n),而汉诺塔问题的状态个数为O(n),时间复杂度却为O(2n),最优二叉查找树的状态个数为O(n2),时间复杂度却为O(n3)。B❌
动态规划有两种实现形式,分别为递归自顶向下(递归+可记忆),递推自底向上(迭代)。C✅
递归和递推只是实现方法不同,状态转移方程都一样,所以时间复杂度是相当的,例如,斐波那契数列。D✅
题型二(贪心法和动态规划对比)
1、以下关于贪心法和动态规划的说法中,错误的是( )。
A、对特定的问题,贪心法不一定适用
B、当特定的问题适用贪心法时,通常比动态规划的时间复杂度更低
C、对很多问题,递推实现和递归实现动态规划方法的时间复杂度相当
D、采用动态规划的算法一定具有多项式时间复杂度
解析:(D)
贪心算法不考虑整体上的综合最优决策,而在局部上以最优决策来解决问题(每次最优),所以对一些特定问题不一定适用。A✅
贪心法通常比动态规划时间复杂度更低,例如,贪心法中的克鲁斯卡尔算法(Kruskal)为O(nlogn),迪杰斯特拉算法(Dijkstra)为O(n),而动态规划中构建最优二叉查找树为O(n3),0-1背包的时间复杂度为O(n×W)。B✅
递归是自顶向下(递归+可记忆),递推(迭代)是自底向上,只是实现方法不同,状态转移方程都一样,所以时间复杂度是相当的,例如,斐波那契数列。C✅
动态规划的时间复杂度取决于具体的问题,时间复杂度不一定是多项式,例如,汉诺塔问题 O(2n) 是指数时间、0-1 背包O(n×W)是伪多项式时间,因为 W 只是数值大小而不是输入位数,当用二进制表示W时(需要log₂W位),所以不是真正的指数时间,也不是真正的多项式时间。D❌
三、分治法
题型一(分治法的概念)
1、分治法的基本步骤不包括()
A、将原问题分解为若干个规模较小的子问题
B、求解每个子问题
C、合并子问题的解得到原问题的解
D、反复试探并回退,细调子问题的解
解析:(D)
分治法主要分为分解、治理两大步骤。分解,是将问题分解成多个规模上较小、内容上相互独立、性质上与原问题相同的子问题;治理,是对各个子问题直接求解,若仍不容易解决,则再进行进一步分解后再求解,由于性质相同,所以求解子问题的解决方法和原问题是相同的;治理中包含合并,是将得到的各个子问题的解进行合并,从而得到原问题的解。所以不包含反复试探并回退的说法。D❌
题型二(分治法的应用)
1、(判断)二分查找算法是分治法的一种应用,其时间复杂度为 O(log2n)。
解析:(√)
二分查找中,当有序序列为 n = 1 时,查找元素 x 需要的时间复杂度为 T(n)=O(1);而当 n > 1 时,子问题的规模为n/2,其递归关系式为 T(n)=T(n/2x)+xO(1),即令n=2x,则 x=log2n,二分查找的 T(n)=O(1)+O(log2n),即二分查找的时间复杂度为 O(log2n)。✅
2、下列算法中,不基于分治法的是()。
A、快速排序
B、归并排序
C、二分查找
D、Dijkstra 算法
解析:(D)
常见的分治法有二分查找(分治 + 缩小范围)、快速排序(分治 + 划分)和归并排序(分治 + 合并)等。A、B、C✅
贪心算法:哈夫曼编码、Prim 算法、Kruskal 算法、Dijkstra 算法 (前者用于哈夫曼树,后三者用于图)。D❌
四、常用搜索算法
题型一(穷举法的概念)
1、(判断)穷举法的时间复杂度通常较高,因此只适用于小规模问题。
解析:(√)
穷举法需要枚举所有可能,时间复杂度多为 O(2n)或 O(n!),仅适合小规模数据。✅
2、要查找有序数组中的特定元素,以下哪种算法思路最合理,且不属于穷举法()。
A、逐一遍历数组中所有元素比对
B、每次将数组拆分为两半,缩小查找范围
C、随机选取元素比对,直到找到目标值
D、从数组两端同时向中间比对
逐一遍历数组中所有元素比对,依次比对,是穷举法。A✅
每次将数组拆分为两半,缩小查找范围。该方法是二分查找,即分治法,不属于穷举法。B❌
随时选取数组的元素比对,其实也是在遍历数组,一直在比对每个元素,而且还可能会重复对比元素,所以也是属于穷举法。C✅
从数组两端同时向中间比对,是按照顺序来进行遍历元素,也没有减少比较元素的次数,还是穷举法。D✅
3、以下算法中,不属于暴力算法的是()。
A、穷举法
B、回溯法
C、分治法
D、枚举法
解析:(C)
穷举法、枚举法、回溯法都属于基于搜索的算法,都是暴力搜索,其中回溯法是优化后的暴力搜索(带剪枝的穷举)。A、B、D✅
而分治法可以理解成分解和治理两步,治理后进行合并,从而将所有子问题的解合并,得到原问题的解,不属于暴力算法。C❌
题型二(回溯法的概念)
1、关于回溯法,下列说法错误的是()
A.、回溯法是一种试探性算法,它的基本思想是尝试所有可能的路径
B、回溯法通常可以通过主动剪枝来大幅减少搜索次数
C、回溯法解决 N 皇后问题时,会尝试在每一行放置皇后,若冲突则回退
D、回溯法不需要考虑问题的约束条件,可直接尝试所有可能
解析:(D)
回溯法通过深度优先搜索(DFS),本质上还是搜索所有可能,找出满足约束条件的所有解,如果该部分解不满足约束条件时剪掉(剪枝)。D❌
题型三(DFS/BFS 搜索)
1、下列关于深度优先搜索(DFS)和广度优先搜索(BFS)的说法,错误的是()。
A、DFS 通常使用栈(或递归栈)实现,BFS 通常使用队列实现
B、BFS 可以保证找到无权图中两点之间的最短路径
C、DFS 和 BFS 的时间复杂度均为 O(n+m)(n 为节点数,m 为边数)
D、DFS 一定不会重复访问同一个节点
解析:(D)
深度优先搜索(DFS)可概括为尽可能深地去搜索一个图,是一个递归的过程(需要用到栈存储),同时采用回溯算法。广度优先搜索是通过队列来存储图的顶点实现遍历(队列用于避免重复访问,存放已经访问过的各邻接顶点)。A✅
BFS 可以保证在无权图中找到两点之间的最短路径,因为是逐层遍历,保证了它找到的第一次到达目标的路径就是最短路径。B✅
对于一个图G =(V,E),由顶点集 V 和边集 E 组成。DFS 的时间复杂度取决于图的存储结构,如果是邻接矩阵存储,时间复杂度为O(∣V2∣),如果是邻接表存储,时间复杂度为O(∣V+E∣);BFS 与 DFS 相同,也是取决于图的存储结构,与它们遍历的顺序无关,因为在访问每个顶点和每条边的开销上是相同的。C✅
深度优先搜索(DFS)会进行回溯,从而沿着原路返回尝试其他分支。D❌
2、求解 " 迷宫中从起点到终点的所有可行路径 ",最适合的算法是()
A、贪心算法
B、深度优先搜索(DFS)
C、广度优先搜索(BFS)
D、动态规划
解析:(B)
题设要求是所有可行路径,而不是一条最短路径或任意一条路径,所以需要系统性地搜索全部可能的路径,贪心是每一步选当前最优,不回溯,只能找一条路径。A❌
深度优先搜索(DFS)是尽可能深地去搜索一个图(先走到底),然后通过回溯,若当前分支已经访问过,则会回溯到上一个顶点,继续搜索其他分支顶点,直到所有顶点被访问。总的来说,进入一个迷宫的分支,记录路径,到终点保存,回溯继续搜索其他路径。B✅
广度优先搜索(BFS)可以找到最短路径,如果要找迷宫所有路径,需要记录所有可能的分支情况,会比 DFS 占用更多,总的来说没有 DFS 更适合。C❌
动态规划会记住子问题的解(记忆),避免重复计算。找迷宫所有可行路径并不需要避免重复计算子路径,因为每条完整路径都要输出,所以动态规划也不合适。D❌
题型四(分支限界法的概念)
1、若用分支限界法求解最大化问题,当某子问题的最优值小于当前下界时,该子问题应( )。
A、继续分支
B、剪枝
C、调整约束
D、重新定界
解析:(B)
当上届大于下届,则说明分支有更好的解,此时才需要继续分支。A❌
上界相当于当前最高要求,下界相当于当前最低要求,小于下届,则不可能比目前得到的结果更优,所以应该剪枝。B✅
若发现松弛解不满足要求,分支限界法才会通过添加新约束。C❌
重新定界是指在求解过程中发现更好的解,从而更新当前最佳值,此时才会进行重新定界。D❌
2、分支限界法求解整数规划问题时,"定界" 的依据是( )。
A、约束条件个数
B、目标函数值
C、决策变量数量
D、可行解数量
解析:(B)
分支限界法需要定界,对于一个待求解的问题,上界和下界是对问题的可能解进行限制和估计的方法,从而用于解决问题。上界相当于当前最高要求,指问题中的最优解不会超过某个已知值,下界相当于当前最低要求,指问题中的最优解不会低于某个已知值。所以不管上界还是下界,它们都是目标函数值,是数值界限。B✅
题型五(分支限界法的应用)
1、常见的分支限界法搜索策略不包括( )。
A、队列式(FIFO)
B、堆栈式(LIFO)
C、优先级队列式
D、深度优先搜索
解析:(D)
深度优先搜索(DFS)本身是一种搜索遍历策略,所以不包括。D❌
题型六(回溯法与分支限界法对比)
1、回溯法与分支限界法的核心区别是( )。
A、回溯法采用深度优先,分支限界法仅用广度优先
B、回溯法不剪枝,分支限界法依赖剪枝
C、回溯法追求所有可行解,分支限界法优先找最优解
D、回溯法处理组合问题,分支限界法处理数值问题
解析:(C)
回溯法采用深度优先,而分支限界法不仅只采用广度优先,也有队列式(FIFO)、堆栈式(LIFO)和优先级队列式。A❌
回溯法通过约束函数进行可行性剪枝,并不是不剪枝;分支限界法通过界限函数剪枝,即最优性剪枝(上届、下届)。B❌
回溯法的优点是能够找出所有可能的解,且不仅仅是最优解。而分支限界法的优点在于它能够在问题规模较大时,通过剪枝有效地减少搜索空间,从而快速找到最优解,但不能保证找到所有的可能解。C✅
回溯法常用于决策问题、枚举所有解、约束满足问题等,而分支限界法适用于带数值目标的最优化问题。D❌
五、算法设计范式对比
1、下列关于算法的说法,错误的是( )。
A、如果有足够的时间和空间,枚举法能解决一切有限的问题
B、分治算法将原问题分为多个子问题进行求解,且分解出的子问题必须相互独立
C、如果能找到合理的贪心原则,贪心算法往往能够比其他方法更快求解
D、倍增法在搜索未知长度的有序数组时,通过动态倍增或减半步长,快速定位目标范围
解析:(B)
枚举法在足够空间复杂度时空下能解决一切有限问题。A✅
分治算法不是必须拆成多个子问题,例如,二分查找是只拆分1个子问题,左边查找或右边查找。B❌
贪心算法每一步都选当下最好,不像枚举尝试所有可能,也不用像动态规划需要记录当前所有状态,所以常比其他方法更快求解。C✅
倍增法是先设置步长大范围找某个数,再逐渐缩小步长精确定位,从而快速确定目标范围。D✅
2、下面哪个不是常见的算法设计范式?
A、分治法
B、动态规划
C、递归
D、随机化
解析:(C)
分治法 、动态规划、随机化算法都是常见的算法设计范式,数值随机化算法、蒙特卡洛算法、拉斯维加斯算法、舍伍德算法等都是随机化算法。A、B、D✅
递归是一种编程实现方法,不是算法设计范式,例如,在分治法、动态规划、回溯法等都可以用递归实现,也可以迭代实现。C❌
3、某道路网络有多个损坏段,修复成本不同,要求用最低成本使道路网络连通,适配的算法范式是()。
A、回溯法
B、贪心算法
C、动态规划
D、枚举法
可以每次选择局部最优的修复方式来进行修复道路,通过Kruskal 算法或 Prim 算法来实现,即贪心算法。B✅