贵有恒,何必三更眠五更起
最无益,莫过一日曝十日寒
- 链表的基本思路是多加指针
- 链表很多是前面+dummy节点(虚拟头节点)
- 链表删除元素:
pre.next =pre.next.next
- 二叉树的种类
- 二叉搜索树
- 广度优先(队列),深度优先(栈)
- 树的常见解决思路: 递归
- 邻接表
- 邻接矩阵
回溯算法
backtracking() {
if (终止条件) {
存放结果;
}
for (选择:选择列表(可以想成树中节点孩子的数量)) {
递归,处理节点;
backtracking();
回溯,撤销处理结果
} }
递归
- 调用自己函数本身,就是递归
终止条件(求解最基本的问题)
递归过程(把原问题转化为更小的问题)
- 计数
- 求最值
- 记忆化搜索 - 从上而下 - 树
- 数学归纳 - 从下到上 - 表
- 最长公共子串
dp[0][j] = 0; (0<=j<=m)
dp[i][0] = 0; (0<=i<=m)
dp[i][j] = dp[i-1][j-1] + 1; (str1[i] == str2[j])
dp[i][j] = 0; (str[i] != str[j])
- 最长公共子序列
dp[0][j] = 0; (0<=j<=m)
dp[i][0] = 0;(0<=i<=m)
dp[i][j] = dp[i-1][j-1] + 1; (str1[i] == str2[j])
dp[i][j] = max(dp[i][j-1],dp[i-1][j]); (str1[i-1]!=str2[j-1])
- 最长递增子序列
F[x] = max(1,F[i] +1 | ai <ax && i<x)
- 最大子序列和
dp[1] = a1; (a1>0 && i==1)
dp[1] = dp[i-1] +ai;(ai>=0 && i>=2)
dp[i]=0; (dp[i-1] + a1 <= 0 && i>=2)
- 数塔问题
dp[i][j] = max(dp[i-1][j-1],dp[i-1][j] + data[i][j])
- 01背包问题
dp[i][j] = max(dp[i-1][j-v[i]]+c[i],dp[i-1][j])
- 从n个数中选择k个数使之和为某个值, 背包问题的变种
F(i,c) = F(i-1,c) || F(i-1,c-v[i])
- 时间复杂度:
O(n * sum)
- 矩阵连乘
dp[i][j]=0; i==j
dp[i][j] = min(dp[i][k] + dp[k+1][j] + p[i-1]*p[k]*p[j]); (i<j && i<=k<j)
dp[1][n] 为最终解
- 暴力解法
- 滑动窗口: 固定长度的滑动窗口,不定长的滑动窗口(快慢指针)
- 深度优先算法(染色问题)
- 双指针
- 极值等问题
- 连续问题(1446)
- 单调栈应用场景: 每个数右边第一个比它大的数
- 深度优先-栈
- 回溯问题:
- 从n个数中随机取t个能否等于某个值
- 很多算法能先排序就先排序
- 输入规模和时间复杂度关系
- 计算机只能暴力计算,但是可能重复计算, 所以需要剪枝
functools 中的 reduce, lru_cache
collections 中的 Counter, defaultdict
itertools 中的 permutations combinations
heapq
bisect中的bisect_left,bisect_right,insort,左右指的是在相同值的左边还是右边
python set的交&,并|,补-, 对称差集^,比较大小<=
python中的按位与&,按位或|,按位异或^,按位取反~,左移<<,右移>>
in /not in
的判断最好是对set进行判断
- 二分查找: O(log(n))
- False == 0,True == 1,这个list[int]的判断条件时很容易错
- 用字典的时候,先看key在不在然后再去判断相应的取值
- 有序list 一定想到bisect
- 最值问题首先想到动态规划
- 没啥想法先用暴力解法
- if not q and not p 两者都是None,有一个为None, if not q or not p