视频地址:https://www.bilibili.com/video/BV1ix411f7aNjava
课程说明算法
前导技能编程
动态规划(Dynamic Programming)数组
目标ide
课程开始函数
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有必定的现金,影响你偷窃的惟一制约因素就是相邻的房屋装有相互连通的防盗系统,若是两间相邻的房屋在同一夜被小偷闯入,系统会自动报警。优化
给定一个表明每一个房屋存放金额的非负整数数组,计算你在不触动警报装置的状况下,可以偷窃到的最高金额。spa
示例 1:设计
输入: [1,2,3,1] 输出: 4 解释: 偷窃 1 号房屋 (金额 = 1) ,而后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:code
输入: [2,7,9,3,1] 输出: 12 解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。
思路:暴力破解法(递归算法)
public class Solution { public int solve(int idx,int[] nums){ if(idx<0){ return 0; } return Math.max(nums[idx]+solve(idx-2,nums),solve(idx-1,nums)); } public int rob(int[] nums){ return solve(nums.length-1,nums); } }
【注】代码超时——\(O(2^n)\)
n-1->(n-3,n-4,n-5...)
n-2->(n-4,n-5...)
深度为 \(n\)
\(O(2^n)\)
本质:递归
原问题(N)->子问题(N-1)->原问题(N)
最优子结构
重叠子问题
递归的意义:递归不是追求效率,递归追求的是咱们怎么看待、拆解这个问题。
处理重叠子问题:(用一个数组来存结果)——计划搜索
public class Solution { public static int[] result;//数组存结果 public int solve(int idx,int[] nums){ if(idx<0){ return 0; } if(result[idx]>=0){ return result[idx]; }//算过了直接从数组取值 result[idx]=Math.max(nums[idx]+solve(idx-2,nums),solve(idx-1,nums)); return result[idx]; } public int rob(int[] nums){ result=new int[nums.length]; for (int i = 0; i < nums.length; i++) { result[i]=-1; }//初始化数组置为-1 return solve(nums.length-1,nums); } }
解:每一个状态只算了一遍—— \(O(n)\)
套路
离散问题
最优子结构
递归改递推(非递归):
自顶向下的解法
public class Solution { public static int[] result;//数组存结果 public int solve(int idx,int[] nums){ if(idx<0){ return 0; } if(result[idx]>=0){ return result[idx]; }//算过了直接从数组取值 result[idx]=Math.max(nums[idx]+solve(idx-2,nums),solve(idx-1,nums)); return result[idx]; } public int rob(int[] nums){ if(nums.length==0){ return 0; } if(nums.length==1){ return nums[0]; } result=new int[nums.length]; result[0]=nums[0]; result[1]=Math.max(nums[0],nums[1]); for (int idx = 2; idx < nums.length; ++idx) { result[idx]=Math.max(nums[idx]+result[idx-2],result[idx-1]); } return result[nums.length-1]; } }
斐波那契数列
N!
暴力破解法:
public class Solution { public int f(int n,int m){ if(n==0 || m==0){ return 0; } if(n==1 || m==1){ return 1; } return f(n-1,m)+f(n,m-1); } }
小偷有一个容量为W的背包,有n件物品,第i个物品价值vi,且重wi
目标:找到xi是的对于全部的xi={0,1}
\(sum(wi*xi)<=W\),而且\(sum(xi*vi)\)最大
套路:最大
Leetcode 322.零钱兑换
给定不一样面额的硬币 coins 和一个总金额 amount。编写一个函数来计算能够凑成总金额所需的最少的硬币个数。若是没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3 输出: -1
暴力破解法:
public class Solution { public static int maxValue=10000000; public int search(int idx,int amount,int[] coins){ if(amount==0){ return 0; } if(amount<0){ return maxValue; } if(idx>=coins.length){ return maxValue; } return Math.min(search(idx,amount-coins[idx],coins)+1,search(idx+1,amount,coins)); } public int coinChange(int[] coins,int amount){ int res=search(0,amount,coins); if(res<maxValue){ return res; }else{ return -1; } } }
超时——优化(空间换时间)
public class Solution { public static int maxValue=10000000; public static int[][] f=new int[1000][10000]; public int search(int idx,int amount,int[] coins){ if(amount==0){ return 0; } if(amount<0){ return maxValue; } if(idx>=coins.length){ return maxValue; } if(f[idx][amount]>=0){ return f[idx][amount]; } f[idx][amount]=Math.min(search(idx,amount-coins[idx],coins)+1,search(idx+1,amount,coins)); return f[idx][amount]; } public int coinChange(int[] coins,int amount){ for (int i = 0; i < 1000; i++) { for(int j=0;j<10000;j++){ f[i][j]=-1; } } int res=search(0,amount,coins); if(res<maxValue){ return res; }else{ return -1; } } }
递归转递推(非递归)省空间
相似LeetCode 72
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操做数 。
你能够对一个单词进行以下三种操做:
插入一个字符
删除一个字符
替换一个字符
示例 1:
输入:word1 = "horse", word2 = "ros" 输出:3 解释: horse -> rorse (将 'h' 替换为 'r') rorse -> rose (删除 'r') rose -> ros (删除 'e')
示例 2:
输入:word1 = "intention", word2 = "execution" 输出:5 解释: intention -> inention (删除 't') inention -> enention (将 'i' 替换为 'e') enention -> exention (将 'n' 替换为 'x') exention -> exection (将 'n' 替换为 'c') exection -> execution (插入 'u')
伪代码
X[n] 取哪些 Y[m] 取哪些 int search(int xi,int yi){ if(xi>n || yi>m){ return -1; } if(xi==n && yi==m){ return 0; } return Math.max( if(X[xi]==Y[yi]) search(xi+1,yi+1)+1, search(xi,yi+1), search(xi+1,yi) ); //search(xi,yi); }
暴力搜索
map[n][n]; map[i][j]-> i-j距离 boolean visit[n]; int search(int idx,int count){ if(count==n){ return 0; } int min=1000000; for(int i=0;i<n;i++){ if(!visit[i]){ visit[i]=true; int t=search(i,count+1)+map[idx][i]; if(t<min){ min=t; } } visit[i]=false;//复原 } return min; }
动态规划 算法用到的题目存在不少套路
滚动数组,状态压缩,升维,单调性,四边形不等式(高级套路)
先学套路,跳出套路
本质:先暴力,找冗余,去冗余