动态规划
1递归+记忆化 ->递推
2状态的定义 opt[n] dp[n] fib[n]
3状态转移方程 opt[n] = best_of(opt[n-1],opt[n-2],.....)
4最优子结构
比如斐波那契数列 N^2时间复杂度
int fib(int n){
return n<=1? n: fib(n-1)+fib(n-2);}
现在加入记忆化,增加缓存
F[0]=0,F[1]=1;
for (int i=0;i<=n;++i){
F[i]=F[i-1]+F[i-2];}
下面一道路径判断题
最后起点到终点,递推 10+17=27种
时间复杂度 O(M*N)
状态方程
opt[i,j]=opt[i-1,j]+opt[i,j-1]
if a[i,j]= "空地":
opt[i,j]=opt[i-1,j]+opt[i,j-1]
else: //石头
opt[i,j]=0
DP 回溯 贪心的区别
回溯(递归) 重复计算
贪心 永远局部最后
DP(动态规划,递推) 记录局部最优子结构/多种记录值
习题:
1爬楼梯,N层一共几种办法
public int climb(int n){
if(n==0||n==1||n==2){return n;}
int[] mem=new int[n];
mem[0]=1;
mem[1]=2;
for(int i =2;i<n;i++){
mem[i]=mem[i-1] +mem[i-2];
}
return mem[n-1];
}PYTHON
def climb(self,n):
x,y=1,1
for _ in range(1,n):
x,y=y,x+y
return y
2 三角型最小路径和
DP[i,j]=min(DP[i+1,j],DP[i+1,j+1])+Triangle[i,j]
DP[m-1,j]=Triangle[m-1,j]
O(M*N)
//C++
class solution{
public:
int mininumTotal(vector<vector<int>> &triangle)
{
vector<int> mini=triangle[triangle.size()-1];
for (int i=triangle.size()-2;i>=0;--i)
for (int j=0;j<triangle[i].size();++j)
mini[j]=triangle[i][j] +min(mini[j],mini[j+1]);
return mini[0];
}
};
PYTHON
def mininumTotal(self,n):
if not triangle: return 0
res =triangle[-1]
for i in xrange(len(triangle)-2,-1,-1):
for j in xrange(len(triangle[i])):
res[j]=min(res[j],res[j+1])+triangle[i][j]
return res[0]
5乘积最大子序列
class maxproduct: def maxproduct(self,nums): if nums is None:return 0 dp =[[0 for _ in range(2)] for _ in range(2)] dp[0][0],dp[0][1],res =nums[0],nums[0],nums[0] for i in range(1,len(nums)): x,y=i%2,(i-1)%2 dp[x][0]=max(dp[y][0]*nums[i],dp[y][1]*nums[i],nums[i]) dp[x][1]=min(dp[y][0]*nums[i],dp[y][1]*nums[i],nums[i]) res=max(res,dp[x][0]) return res A=maxproduct() print(A.maxproduct([1,2,3,4,-1,-2]))
class maxproduct: def maxproduct(self,nums): if nums is None:return 0 curMax,curMin,res =nums[0],nums[0],nums[0] for i in range(1,len(nums)): num=nums[i] curMax,curMin=curMax *num,curMin*num curMax,curMin=max(curMax,curMin,num),min(curMax,curMin,num) res=curMax if curMax>res else res return res A=maxproduct() print(A.maxproduct([1,2,3,4,-1,-2]))
2股票问题
class gupiao: def maxProfit(self,prices): if not prices:return 0 res=0 profit=[[0 for _ in range(3)] for _ in range(len(prices))] profit[0][0],profit[0][1],profit[0][2]=0,-prices[0],0 // 第二位状态分别是 没有买入股票 买入一股还没卖 买入一股卖了 for i in range(1,len(prices)): profit[i][0]=prices[i-1][0] //前一天的状态 profit[i][1]=max(profit[i-1][1],profit[i-1][0]-prices[i]) //前一天买的今天不动或者今天买入 profit[i][2]=profit[i-1][1]+prices[i] //套现 res=max(res,profit[i][0],profit[i][1],profit[i][2]) return res
最长上升子序列
暴力法 2^n
DP n^2
二分法nlogn
DP法
public int lengthLIS(int[] nums){
if (nums ==null || nums.length ==0){
return 0;
}
int res =1;
int[] dp=new int[nums.length +1];
for (int i=0;i<nums.length;i++) dp[i]=1;for(int i=1;i< nums.length;++i){
for (int j =0;j<nums.length;++i){
if (nums[j]<nums[i]){
dp[i]=Math.max(dp[i],dp[j]+1);
}
}
res=Math.max(res,dp[i]);
}
return res;
}
二分法
int lengthLIS(vector<int>& nums){
vector<int> res;
for (int i=0;i<nums.size();i++){//C++中的二分查找 lower_bound 找到最小的比nums[i]大的数
auto it =std::lower_bound(res.begin(),res.end(),nums[i]);
if(it==res.end())
res.push_back(nums[i]);//增加数组元素
else *it = nums[i]; //替换
}
return res.size();
}
零钱兑换
比如面值[1,2,5] 拼成11
贪心:每次选满最大面值
DP:看成上台阶,每次最少的步数
int coinChange(vector<int>& coins, int amount){
int Max=amount+1;
vector<int> dp(amount+1,Max);
dp[0]=0;
for (int i=1;i<=amount;i++){
for(int j=0;j<coins.size();j++){
if (coins[j]<=i){
dp[i]=min(dp[i],dp[i-coins[j]]+1);
}
}}
return dp[amount]>amount ? -1:dp[amount];
}
编辑距离
两个单词,单词1变为单词2,最少需要多少操作
horse -> ros 只用插入删除替换
1暴力法 BFS广度优先
2DP 两部曲
a 状态 dp[i][j]
i表示单词1 前i个字符,j表示单词2前j个字符
单词1 前i个字符 它匹配单词2前j个字符
结果DP[m][n] 变动次数
bDP方程
DP[i][j]=
for i 0->m
for j 0->n
if w1[i]==w2[j]
DP[i][j]=DP[i-1][j-1]
else: // 插 删 替
DP[i][j]=Min(DP[i-1][j],DP[i][j-1],DP[i-1][j-1])+1
def minDistance(self,word1,word2):
m,n=len(word1),len(word2)
dp=[[o for _ in range(n+1)] for _ in range(m+1)]for i in range(m+1):dp[i][0]=i
for j in range(m+1):dp[0][j]=jfor i in range(1,m+1):
for j in range(1,n+1):
dp[i][j]=min(dp[i-1][j-1] + (0 if word1[i-1] == word2[j-1] else 1),
dp[i-1][j]+1,
dp[i][j-1]+1)
return dp[m][n]
并查集
find 确定元素属于哪一个子集
union 将两个子集合并成同一个集合
经常解决的问题
1 小弟-》老大
2帮派识别
3两种优化方法 合并&路径压缩
岛屿个数
朋友圈
Cache缓存
记忆
钱包-存储柜
代码模块
class LRU(object): def __init__(self,capacity): self.dic=collections.OrderedDict() self.remain=capacity def get(self,key): if key not in self.dic: return -1 v=self.dic.pop(key) self.dic[key]=v return v def put(self,key,value): if key in self.dic: self.dic.pop(key) else: if self.remain >0: self.remain -=1 else: self.dic.popitem(last=False) self.dic[key]=value
布隆过滤器