递归函数
- 递归常用于二叉树相关的题目,以实现深度优先遍历
- 递归三要素
- 返回值和形参
- 递归的退出条件
- 单层递归的逻辑(本层递归要干什么+调用下一层递归)
降低时间复杂度的方法
- 双指针法(常用于数组、链表、字符串)
- 数组中,可以用来移除元素的时候降低时间复杂度,通过在一个循环里完成暴力解法两个循环才能完成的事情
- 字符串中,反转字符串、替换字符等操作都可以使用到双指针
- 链表里,反转链表、判断是否有环以及找环的入口都要用到双指针(快慢指针)(在链表中,与其它数据结构的题目不同,使用双指针不仅仅是为了降低时间复杂度,更是因为很多题只能利用双指针的解法)
- 计算N数之和时,使用双指针可以将时间复杂度降为O(n^(N-1)),同时也更便于进行N元组去重操作
- 哈希法(适用于查询元素的场景)
固定规律处理某类数据时
- 考虑在for循环上做文章,从而避免复杂的if-else判断,如每隔k个字符处理一次,可以直接把for循环的步长设为k
相邻元素的处理(如匹配、消除等)
- 使用栈结构或双指针法(栈结构的总结见下)
字符串类题目
- 双指针法很常用
- KMP法对于字符串匹配很重要
栈、队列总结
栈与队列做一个总结吧,加油
https://programmercarl.com/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97%E6%80%BB%E7%BB%93.html
- 栈的经典应用
- 括号语句的合法性判断(括号匹配)
- linux里文件路径的切换
- 递归函数的实现
- 队列的应用
- 滑动窗口最大值
- K个高频元素
二叉树类题目
- 从上向下处理类
- 如求二叉树深度、构建二叉树类题目
- 使用前序遍历
- 优先构建中节点,再构建左右节点
- 二叉搜索树类题目
- 常使用中序遍历,刚好契合二叉搜索树左小右大的特性,此时遍历的元素数值刚好从小到大排序
- 但有时候,也要用从上向下处理(前序)的方式,如寻找二叉搜索树中的最近公共祖先(力扣235)、力扣701.二叉搜索树中的插入操作
- 还是要根据题意思考利用二叉搜索树的何种特性,并采取针对性的遍历方式
- 从下向上处理类
- 如求二叉树的高度、求两个节点的最近公共祖先(力扣236)
- 使用后序遍历
- 有时候会用到回溯
回溯算法题目
- 看到以下5类题目,想到使用回溯:
- 组合:给一个数组,求满足要求的子组合:逐个元素加入子组合来实现(收集叶子节点)
- 排列:给一个数组,求满足要求的排列:逐个元素加入排列来实现(收集叶子节点)
- 切割:给一个字符串,求满足要求的切割方式:遍历不同的切割区间大小来实现
- 子集:给一个集合,求满足要求的子集:遍历收集树的每个节点
- 棋盘问题:N皇后,数独等:回溯for循环的范围是棋盘的宽度,递归的深度是棋盘的高度
- 对于回溯类题目,如果没有思路:
- 找一个具有普遍意义的实例
- 逐层画树状图
- 分析,即可找到思路
- 回溯优化:
- 回溯算法优化一般就是指剪枝操作
- 剪枝的对象常常是循环的范围(剩余集合内的元素不足以满足题目要求时,就不再遍历了)
- 回溯问题的去重:
- 树层去重和树枝去重(画图辅助理解),详见文章
- 常使用used数组来辅助判断是树层还是树枝,如果形参有start_index,也可以通过start_index去重
- 总结
贪心算法总结
- 无固定套路,总的来说基本思想就是先寻找局部最优,再推及全局最优
重叠区间类问题
- 重叠区间类问题只需要遍历整个数组一次,在遍历过程中通过维护区间的起点和终点来记录结果
- 常见题目如452. 用最少数量的箭引爆气球、763.划分字母区间、56. 合并区间
动态规划总结
- 背包问题
- 背包问题最难的就是,如何把一道应用题转化为背包问题来解决
- 想到一道题要用动态规划,然后又想到这道题涉及到了从一个数组里拿元素来满足某种要求,可以考虑使用背包的思路
- 找到什么是背包容量,什么是物品,物品能拿取1次还是多次,具体是问能否装满背包还是装满背包最多/最少物品等等
- 找到之后写出来递推公式,思考清楚初始化和遍历顺序,本题即可解决
- 01背包(几类主要的应用)
- 题目描述:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
- 纯01背包类问题
- 给一个背包和一堆物品,问装满背包能得到的最大价值是多少
- 416.分割等和子集
- 给一个背包和一堆物品,看能不能装满这个背包(重点在能否)
- 1049.最后一块石头的重量
- 给一个背包和一堆物品,看最满能装多少(重点在最大值)
- 494.目标和
- 给一个背包和一堆物品,看有多少个组合方式能装满背包
- 474.一和零
- 给一个背包和一堆物品,看装满背包最多有多少种物品
- 完全背包
- 题目描述:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品可以重复使用,求解怎么装背包里物品价值总和最大。
- 纯完全背包类题目
- 解法大体和01背包的解法相同,以下以一维数组解法为例,其和01背包的解法主要有两点不同:
- 一是内层for循环从逆向遍历改为正向遍历,因为完全背包里的每个物品是可以多次放入背包中的
- 二是对于纯完全背包问题,内外层for循环是可以颠倒的,也就是说先遍历物品和先遍历背包容量都可以,其中为什么先遍历背包容量也可以呢?这样不会导致过程中某些dp[j]依赖于了下一个物品的dp[j]吗?其实是没关系的,无论是否依赖了下一个物品,我们最终得到的最后的那个结果值都不会变,也就是说过程中即便发生了变化,结果仍然是正确的。但从理论上来讲,其实还是应该先遍历物品,这样每一步都是正确的,不会产生困惑
- 解法大体和01背包的解法相同,以下以一维数组解法为例,其和01背包的解法主要有两点不同:
- 518 . 零钱兑换 II 和 377. 组合总和 Ⅳ
- 给一个背包和一堆物品,看有多少个组合/排列方式能装满背包
- 先遍历物品,再遍历背包容量,得到的结果是组合数——因为此时在不同种背包容量的情况里,都是先放进物品1再放进物品2,不会出现先放2再放1的情况,此时得到的组合之间不会出现顺序不同、元素相同的情况
- 先遍历背包容量,再遍历物品,得到的结果是排列数——因为此时在背包容量递增的过程中,物品1和物品2放入的先后顺序不一定,可能先放了1也可能先放了2,因此集合之间会出现顺序不同、元素相同的情况,也就是得到的结果是排列数
- 给一个背包和一堆物品,看有多少个组合/排列方式能装满背包
- 322 . 零钱兑换 | 279.完全平方数
- 给一个背包和一堆物品(无限拿取),求装满背包所用的最少物品数量(最少数量既不是组合问题也不是排列问题,因为内外层循环可以相互颠倒)
- 背包问题最难的就是,如何把一道应用题转化为背包问题来解决
- 打家劫舍问题
- 每天的状态依赖前两天的状态进行递推
- 普通一维数组结构的打家劫舍问题不难,难点在于解决环结构和二叉树结构的打家劫舍问题
- 环结构拆分为一维数组结构进行解决
- 二叉树结构综合二叉树遍历方案和对每个节点递推公式的分析来进行解决
- 股票买卖问题
- 每天分为持有和不持有股票的状态(如果限制买卖总次数可以添加第几次持有/不持有的状态)
- 依赖前一天的状态进行递推(区分好股票可以买一次和多次的情况,只能买一次,则手头的现金数额一定是0,如果能买多次,则手头的现金数额可能为前一天卖出以后的利润)
- 子序列问题
- 回文子串类问题
- 常常需要定义二维dp数组,分别表示字符串的起始和终止位置
- 关键在于判断起始元素和终止元素是否相同,并对不同的状态采取不同的递推策略
- 回文子串类问题