𝓝𝓮𝓶𝓸&博客

【算法】常用总结

注意事项

  1. 做算法题时,一定要注意对结尾边界数据操作的特殊性,一定要检查一下结尾边界数据是否能够覆盖到,并且满足题意。开头的数据是最容易注意到的,中间过程的数据是我们会专心花精力去做的,结尾的边界数据我们往往会忽略,所以请一定注意。

  2. 有时候,我们需要对前后两个元素进行判断操作,我们可以不用从第一个元素开始遍历(即 从0开始遍历),我们当前元素可以从第二个元素开始(即 从1开始遍历),前驱元素我们就可以直接初始化为第一个元素,这样我们会方便许多,并且不需要判断越界问题,如果从0开始,操作后一个元素,到最后一个元素时就没有后一个元素了,就越界了。
    char preC、char c、char nextC

  3. 拿到题先不要着急着做,也不要想一步做一步,想到最后可能就乱了套了,思路全忘了;所以最好先把所有步骤用注释写出来,直到逻辑通顺得完成了整个题目,最后慢慢一步一步实现比较好。

  4. 如果数组计数是连续的,那就可以赊账,记住开始下标,相减即可得到数量,会方便很多。

  5. 数组[]的长度,有length长度属性来获取;
    而引用数据类型(如 String)的长度,有length()方法来获取;
    集合的长度(其实应该说是容量),有size()方法来获取。

  6. 方法获取数据前,最好判断一下是否为null,以免空指针异常。

  7. 如果在数组中我们需要用到i - 1或者i + 1,我们最好加上i > 0或者i < nums.length再进行使用,避免越界。如 if (i > 0 && nums[i] == nums[i-1])

  8. 我们使用 while() 函数来进行遍历时,要特别注意指针 p 是否会越界,最好加上p < nums.length或者p >= 0这种限制!
    这种越界问题特别常出现在使用双指针的时候!
    在循环体中使用的指针都应该在循环条件中标明一下界限范围,以免超过界限,造成越界错误或空指针异常。

数学运算技巧

  1. 除法向上取整:即 天花板除,p / k 需要向上取整,我们可以使用 (p + K - 1) / K ,或者 (p + K - 1) / K = (p-1) / K + 1;
    可以看到,其实就是加了一个 k - 1,再除以 k,也就是说加了一个大于 0.5,小于 1 的数,向上取整。
    相当于 Math.ceil(pile * 1.0 / speed)

  2. 除法向下取整:即 地板除,这就不用说了,Java整型自带的就是向下取整,p / k

  3. 如果我们需要折半二分查找,我们可以使用 mid = left + (right - left) / 2 是一样的效果,因为如果使用 mid = (left + right) / 2 的话,left + right 是有可能溢出内存的。

分治算法 贪心算法 动态规划 回溯法
问题类型 通用问题 优化问题 优化问题 通用问题
子问题结构 独立,类型相同 只考虑当前子问题 子问题可能重复
子问题在最优解里 全部 部分 全部
选择与求解次序 先选择后解决子问题 先选择后解决子问题 先选择后解决子问题 用深度搜索来解决问题

分治算法

  • 基本思想:
    分治法(Divide and Conquer),字面意思就是分而治之,即 将问题简化为“”和“”两个步骤。当遇到一个复杂的问题(规模为N),可以根据问题的性质将其分解为两个或者多个规模较小的子问题(加上一共有K个子问题)。

  • 具体操作:

    1. 将复杂问题拆解为子问题;
    2. 将简单子问题求解;
    3. 将子问题的解合并为原问题的解。
  • 适用性:
    通常来说可以适用分治法的问题是问题规模小时容易解决,而且分解后的子问题性质相同的独立问题
    在算法题中如果出现了O(logn)的复杂度,都可以先考虑一下分治法。


  • 算法应用:

    快速幂,求\(a^n\)(n为偶数)

    用算法设计的思想,不全部计算出来求3的96次方的第十位数值。

    \(3^{96}=9^{48}=81^{24}=6561^{12}=...\)
    \(A^n=A^{n/2}*A^{n/2}\),一直递归下去,其间就可以省略很多不必要的计算,得到O(logn)的方法。

贪心算法

  • 基本思想:
    贪心算法(Greedy algorithm),该算法的名字很形象地说明该算法的特点,将问题分为一个个局部子问题之后,求解每个子问题的时候,不考虑子问题当前的解对于整个问题的影响只考虑当前情况的最优解,即 当前子问题的解不会对后序的状态造成影响。

  • 具体操作:

    1. 将复杂问题分解为多个子问题;
    2. 子问题的解是当前所有解中的最优解;
    3. 将所有子问题的解合并为原问题的解。

    注意:贪心算法与动态规划算法较为相似,其中一个很关键的区别是贪心算法是不可取消的,自顶向下地求出子问题的当前最优解;而动态规划是每步做出的选择都依赖于子问题的解。

  • 适用性:

动态规划

  • 基本思想:
    动态规划算法与之前的分治和贪心算法有点相似,都是将一个规模大的问题转化为几个小的问题,通过解决小问题来得到整体的解。动态规划的核心问题是问题的状态的定义与状态转移房产的求解。动态规划的关键就在于将重复出现的子问题在第一次求解之后就将其保存起来,以后再遇到时不用重复求解。动态规划是按照自底向上的方式计算最优解。

  • 具体操作:

    1. 将问题分解为不同的子问题;
    2. 定义状态找出初始状态;
    3. 状态转移方程的求解;
    4. 求出问题最终答案。
  • 适用性:
    当题目求解的是最大值/最小值可行与否或者方案总数时,考虑使用动态规划算法。

回溯算法

  • 基本思想:
    回溯算法通常用于解决复杂的规模大的问题。回溯算法使用的是深度优先搜索(DFS)的策略。
    所有的可能情况(即 解空间)构成一个搜索树,我们从根结点开始,按照问题的搜索条件向下进行结点选择,在当前结点如果满足解的条件时,就继续向下探索,如果不满足解的条件,那么就回溯到当前结点的父结点,选择另一条路径再继续搜索下去。

  • 具体操作:

    1. 针对所给的问题,确定问题的解空间:首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解;
    2. 确定结点的扩展搜索规则;
    3. 以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
  • 适用性:
    适用于解决复杂的规模大的问题。

posted @ 2019-08-14 13:12  Nemo&  阅读(299)  评论(0编辑  收藏  举报