15 数据结构及算法应用
15 数据结构及算法应用
15.1 算法策略区分
15.1.1、分治法
特征:把一个问题拆分成多个小规模的相同子问题,一般可用递归解决。
经典问题:斐波那契数列、归并排序、快速排序、矩阵乘法、二分搜索、大整数乘法、汉诺塔。
15.1.2、贪心法
(一般用于求满意解)
特征:局部最优,但整体不见得最优。每步有明确的,既定的策略。
经典问题:背包问题(如装箱)、多机调度、找零钱问题。
15.1.3、动态规划法
(用于求最优解)--“最优子结构”和递归式
特征:划分子问题,并把子问题结果使用数组存储,利用查询子问题结果构造最终问题结果。(一般自顶向下时间复杂度为O(2^n),自底向上时间复杂度为O(n^a)效率更高)
经典问题:斐波那契数列、矩阵乘法、背包问题、LCS最长公共子序列
15.1.4、回溯法
特征:系统的搜索一个问题的所有解或任一解。
经典问题:N皇后问题、迷宫、背包问题
15.1.5 特征总结
算法策略判断:
1、回溯,大家比较熟悉,有尝试探索和回退的过程。这个比较好识别。2、分治,分治与动态规划法其实最难区分,分治不好解决问题,从而记录中间解解决问题,有了动态规划法,但是在软设考试中,分治目前只有二分的思想,二分以外的思想都用动态规划法解决了。二分的时间复杂度,与O(
)相关,注意有没有外层嵌套循环。(结合归并排序、快速排序的过程,也是二分的)
3、动态规划法,有递归式,经常考查递归式,此时自底向上实现时,中间解基本上是查表可得的,所以时间复杂度一般是O(n^k),具体k是多少,根据for循环的嵌套。此时循环变量能够看到,是从0或1开始,到n结束,这种情况就是从小规模到大规模,自底向上的。如果用到自顶向下,其实跟分治的实现就差不多了,查表的意义基本上可以忽略不计了,时间复杂度为O(2^n),递归的变量一般由n开始,向1缩小,所以是大规模到小规模,也就是自顶向下的。(一般动态规划法涉及递归式,递归式还会经常用在代码填空中让大家补充缺失的填空,然后会有“最优子结构”的描述,会有表(数组)记录中间解。)
4、贪心法,有时候也会出现“最优子结构”的描述,但是没有递归式。考虑的是当前最优,求得的是满意解。(特殊情况下,贪心法有时候得到的也可以是最优解,比如部分背包问题)
15.2 时间复杂度与空间复杂度
时间复杂度是指程序运行从开始到结束所需要的时间。通常分析时间复杂度的方法是从算法中选取一种对于所研究的问题来说是基本运算的操作,以该操作重复执行的次数作为算法的时间度量。
常见的对算法执行所需时间的度量:
空间复杂度是指对一个算法在运行过程中临时占用存储空间大小的度量。一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小
15.3 代码填空技巧
仔细审题:
1、检查所有用到的变量是否有声明,是否赋初值;
2、检查是否有返回值,与题干要求返回变量名或上下文是否一致;3、检查for循环是否有计数变量的赋值、初值和终止条件;
4、注意while循环的开始和结束;5、有一些变量名具有特殊含义,比如一般用max/min保存最大值/最小值,temp作为中间变量,一般用来存储中间值或用来做数值交换的中间过渡。x>max,则修正max=x
x<min,则修正min=x。6、对特殊的算法策略:回溯法是否有回退k=k-1;对分治法递归的递归调用(调用自身函数名);对动态规划法的查表操作。
7、注意题干描述和代码说明、递归式(条件和等式)、代码中的注释、代码上下文。一般特殊数据结构调用方式会在代码说明或代码上下文中给出。(1)题干公式很重要,一般公式体现在代码中,会有循环边界、判断条件等;
(2)代码说明很重要,一般代码说明会指出一些变量的定义,初始值和边界值;
(3)代码上下文很重要,可以根据上下文判断有没有缺失变量声明、变量赋值;
(4)题干说明很重要,题干有时候也会给出循环边界、判断条件等内容,还可以根据题干描述,判断使用的算法策略,不同的算法策略,一般会有一些典型的代码缺失,比如:动态规划法可能会考查题干给出的递归式以及最优解的判断,分治法一般也会考查到递归式以及问题的划分,贪心法一般会考查满意解的当前最优判断条件,回溯法一般会考查找和回退的过程。