基本思想
备忘录方法
面试题
public int climbStairs(int n) {
if(n<=1) return 1;
int[] fibonacci=new int[n+2];
fibonacci[0]=0;fibonacci[1]=1;
for(int i=2;i<=n+1;i++){
fibonacci[i]=fibonacci[i-1]+fibonacci[i-2];
}
return fibonacci[n+1];
}
定义:min[i]表示第i天前最低股票价格,i>=1(i从0开始)
public int maxProfit(int[] prices) {
if(prices.length<2) return 0;
int[] minBefore=new int[prices.length];
minBefore[0]=prices[0];
for(int i=1;i<prices.length;i++){
minBefore[i]=Math.min(prices[i], minBefore[i-1]);
}
int maxProfit=0;
for(int i=1;i<prices.length;i++){
maxProfit=Math.max(maxProfit, prices[i]-minBefore[i]);
}
return maxProfit;
}
分析
动态规划算法
定义:d[i]表示i的最小平方划分次数,1<=i<=n
初始化:i=k*k,d[i]=1。
递推表达式:当d[i]!=1,d[i]=min{d[k]+d[j-k]},1<=k<i;当d[i]=1,d[i]=1。
空间复杂度O(n),时间复杂度O(n^2)。
<span style="font-size:14px;">public class Solution {
public int numSquares(int n) {
int[] d=new int[n+1];
for(int i=1;i*i<=n;i++){//首先标记所有平方
d[i*i]=1;
}
for(int i=2;i<=n;i++){
if(d[i]==1){
continue;
}else{
int begin=1,end=i-1,min=Integer.MAX_VALUE;
while(begin<=end){
min=Math.min(min, d[begin]+d[end]);
begin++;end--;
}
d[i]=min;
}
}
return d[n];
}
}</span>
分析
定义:sum[i]表示nums[0]~nums[i]的和,i>=1
初始化:sum[0]=nums[0],便于边界处理
递归表达式:sum[i]=sum[i-1]+nums[i],i>=1
public class NumArray {
private int[] nums;
private int[] sumBefore;
public NumArray(int[] nums) {
this.nums=nums;
if(nums.length!=0){
this.sumBefore=new int[nums.length];
solve();
}
}
public int sumRange(int i, int j) {
if(nums.length==0) return 0;
return nums[i]+sumBefore[j]-sumBefore[i];
}
private void solve(){
sumBefore[0]=nums[0];
int sum=nums[0];
for(int i=1;i<nums.length;i++){
sum+=nums[i];
sumBefore[i]=sum;
}
}
}
public int numDecodings(String s) {
if(s.length()==0)return 0;
if(s.length()==1){
return s.charAt(0)=='0'?0:1;
}
int[] count=new int[s.length()];
count[0]=s.charAt(0)=='0'?0:1;
if(s.charAt(1)!='0'){
count[1]+=count[0];
}
if(s.charAt(0)=='1'||(s.charAt(0)=='2'&&s.charAt(1)<='6')){
count[1]+=1;
}
for(int i=2;i<s.length();i++){
if(s.charAt(i)!='0'){
count[i]+=count[i-1];
}
if(s.charAt(i-1)=='1'||(s.charAt(i-1)=='2'&&s.charAt(i)<='6')){
count[i]+=count[i-2];
}
}
return count[s.length()-1];
}
分析
public int lengthOfLIS(int[] nums) {
if(nums.length==0) return 0;
return maxLengthIncreasing(nums).size();
}
public List<Integer> maxLengthIncreasing(int[] a){
int[] d=new int[a.length];
//初始化
for(int i=0;i<d.length;i++) d[i]=1;
//迭代
for(int i=0;i<a.length;i++){
for(int j=0;j<i;j++){
if(a[j]<a[i]){
d[i]=Math.max(d[i], d[j]+1);
}
}
}
//遍历迭代结果
int maxIndex=0;
for(int i=0;i<d.length;i++){
if(d[i]>d[maxIndex]){
maxIndex=i;
}
}
//解析结果
List<Integer> res=new ArrayList<Integer>();
res.add(a[maxIndex]);
int nextIndex=maxIndex;
for(int i=maxIndex-1;i>=0;i--){
if(d[i]+1==d[nextIndex]&&a[i]<a[nextIndex]){
res.add(a[i]);
nextIndex=i;
}
}
Collections.reverse(res);
return res;
}
public int maxSubArray(int[] nums) {
if(nums.length==0) return 0;
int[] d=new int[nums.length];
d[0]=nums[0];
for(int i=1;i<nums.length;i++){
d[i]=Math.max(d[i-1]+nums[i], nums[i]);
}
int max=Integer.MIN_VALUE;
for(int i=0;i<d.length;i++){
max=Math.max(max, d[i]);
}
return max;
}
方案二
我们确定需要一个数组来记录以每个位置结尾的序列的最大和吗?空间复杂度是否可以进行优化?
public int maxSubArray(int[] nums) {
if(nums.length==0) return 0;
int currentMax=nums[0],max=nums[0];
for(int i=1;i<nums.length;i++){
currentMax=Math.max(nums[i], nums[i]+currentMax);
max=Math.max(max, currentMax);
}
return max;
}
//每一时刻,我们只需要保留当前的极值,只有极大值和极小值才可能组合成最终的结果
public int maxProduct(int[] A) {
if (A.length == 0) {
return 0;
}
int maxherepre = A[0];
int minherepre = A[0];
int maxsofar = A[0];
int maxhere, minhere;
for (int i = 1; i < A.length; i++) {
maxhere = Math.max(Math.max(maxherepre * A[i], minherepre * A[i]), A[i]);
minhere = Math.min(Math.min(maxherepre * A[i], minherepre * A[i]), A[i]);
maxsofar = Math.max(maxhere, maxsofar);
maxherepre = maxhere;
minherepre = minhere;
}
return maxsofar;
}
public String longestPalindrome(String s) {
if(s.length()==0) return "";
char[] cs=new char[s.length()*2+1];
cs[0]='*';
int index=1;
for(int i=0;i<s.length();i++){
cs[index++]=s.charAt(i);
cs[index++]='*';
}
int maxStart=0,maxEnd=0;
for(int i=0;i<cs.length;i++){
int start=i,end=i;
while(start>=0&&end<=cs.length-1&&cs[start]==cs[end]){
if(end-start>maxEnd-maxStart){
maxEnd=end;
maxStart=start;
}
start--;
end++;
}
}
StringBuilder builder=new StringBuilder();
for(int i=maxStart;i<=maxEnd;i++){
if(cs[i]!='*')
builder.append(cs[i]);
}
return builder.toString();
}
public String longestPalindrome1(String s) {
if(s.length()==0)return "";
char[] cs=new char[s.length()*2+1];
cs[0]='*';
int index=1;
for(int i=0;i<s.length();i++){
cs[index++]=s.charAt(i);
cs[index++]='*';
}
System.out.println(cs);
boolean[][] d=new boolean[cs.length][cs.length];
for(int i=0;i<cs.length;i++){
d[i][i]=true;
}
//处理每条斜对角线
int maxStart=0,maxEnd=0;
for(int i=1;i<cs.length;i++){
int row=0,col=i;
while(row<cs.length&&col<cs.length){
if(cs[row]==cs[col]&&d[row+1][col-1]==true){
if(col-row+1>maxEnd-maxStart+1){
maxStart=row;
maxEnd=col;
}
d[row++][col++]=true;
}else{
d[row++][col++]=false;
}
}
}
StringBuilder builder=new StringBuilder();
for(int i=maxStart;i<=maxEnd;i++){
if(cs[i]!='*')
builder.append(cs[i]);
}
return builder.toString();
}
public int minCut(String s) {
int n=s.length();
if(s.length()<=1) return 0;
//isPalin[i][j]=ture表示s[i]~s[j]是回文,否则不是
boolean[][] isPalin=new boolean[n][n];
//单个字符为回文
for(int i=0;i<n;i++){
isPalin[i][i]=true;
}
//连续两个字符为回文
for(int i=0;i<n-1;i++){
if(s.charAt(i)==s.charAt(i+1)){
isPalin[i][i+1]=true;
}
}
//从下往上遍历每行
for(int i=n-3;i>=0;i--){
for(int j=i+2;j<n;j++){
if(s.charAt(i)==s.charAt(j)&&isPalin[i+1][j-1]){
isPalin[i][j]=true;
}
}
}
//d[i]表示s[0]~s[i]最小分割次数
int[] d=new int[n];
for(int i=0;i<n;i++){
d[i]=i;
}
for(int i=1;i<n;i++){
if(isPalin[0][i]){
d[i]=0;
continue;
}
for(int j=0;j<i;j++){
if(isPalin[j+1][i]){
d[i]=Math.min(d[i], d[j]+1);
}
}
}
return d[n-1];
}
There are a row of n houses, each house can be painted with one of the three colors: red, blue or green. The cost of painting each house with a certain color is different. You have to paint all the houses such that no two adjacent houses have the same color.
The cost of painting each house with a certain color is represented by a n x 3
cost matrix. For example, costs[0][0]
is the cost of painting house 0 with color red; costs[1][2]
is the cost of painting house 1 with color green, and so on... Find the minimum cost to paint all houses.
Note:
All costs are positive integers.
There are a row of n houses, each house can be painted with one of the k colors. The cost of painting each house with a certain color is different. You have to paint all the houses such that no two adjacent houses have the same color.
The cost of painting each house with a certain color is represented by a n x k
cost matrix. For example, costs[0][0]
is the cost of painting house 0 with color 0; costs[1][2]
is the cost of painting house 1 with color 2, and so on... Find the minimum cost to paint all houses.
Note:
All costs are positive integers.
Follow up:
Could you solve it in O(nk) runtime?
public int minCost(int[][] cost){
if(cost.length==0) return 0;
int H=cost.length,C=cost[0].length;
//d[i][j]表示h[i]刷c[j]时的h[0]~h[i]最小成本
int[][] d=new int[H][C];
for(int c=0;c<C;c++){
d[0][c]=cost[0][c];
}
for(int h=1;h<H;h++){
for(int c=0;c<C;c++){
int minCost=Integer.MAX_VALUE;
for(int k=0;k<C;k++){
if(c!=k){
minCost=Math.min(minCost, d[h-1][k]+cost[h][c]);
}
}
d[h][c]=minCost;
}
}
int min=Integer.MAX_VALUE;
for(int c=0;c<C;c++){
min=Math.min(min, d[H-1][c]);
}
return min;
}
最长公共子序列
public String lcs(String s,String t){
if(s.length()==0||t.length()==0){
return "";
}
int m=s.length(),n=t.length();
int[][] d=new int[m+1][n+1];
//初始化
for(int i=0;i<=m;i++) d[i][0]=0;
for(int j=0;j<=n;j++) d[0][j]=0;
//迭代求解
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(s.charAt(i-1)==t.charAt(j-1)){
d[i][j]=d[i-1][j-1]+1;
}else{
d[i][j]=Math.max(d[i][j-1], d[i-1][j]);
}
}
}
//反向解析结果
StringBuilder res=new StringBuilder();
int row=m,col=n;
while(row>=1&&col>=1){
if(s.charAt(row-1)==t.charAt(col-1)){
res.append(s.charAt(row-1));
row--;col--;
}else{
if(d[row][col-1]>d[row-1][col]){
col--;
}else{
row--;
}
}
}
return res.reverse().toString();
}
分析
public String lcs(String s,String t){
if(s.length()==0||t.length()==0){
return "";
}
int m=s.length(),n=t.length();
int[][] d=new int[m+1][n+1];
//初始化
for(int i=0;i<=m;i++) d[i][0]=0;
for(int j=0;j<=n;j++) d[0][j]=0;
int maxi=-1,maxj=-1,maxLength=Integer.MIN_VALUE;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(s.charAt(i-1)==t.charAt(j-1)){
d[i][j]=d[i-1][j-1]+1;
if(d[i][j]>maxLength){
maxi=i;maxj=j;maxLength=d[i][j];
}
}else{
d[i][j]=0;
}
}
}
if(maxi==-1){
return "";
}else{
return s.substring(maxi-maxLength,maxi);
}
}
分析
public int numDistinct(String s, String t) {
if(s.length()==0||t.length()==0||s.length()<t.length()){
return 0;
}
int m=s.length(),n=t.length();
int[][] d=new int[m+1][n+1];
//初始化
for(int i=0;i<=m;i++) d[i][0]=1;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(i<j){
d[i][j]=0;
}else{
if(s.charAt(i-1)==t.charAt(j-1)){
d[i][j]=d[i-1][j-1]+d[i-1][j];
}else{
d[i][j]=d[i-1][j];
}
}
}
}
return d[m][n];
}
分析
public int minDistance(String word1, String word2) {
if(word1.length()==0){
return word2.length();
}
if(word2.length()==0){
return word1.length();
}
int m=word1.length(),n=word2.length();
int[][] d=new int[m+1][n+1];
for(int i=0;i<=m;i++)d[i][0]=i;
for(int j=0;j<=n;j++) d[0][j]=j;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(word1.charAt(i-1)==word2.charAt(j-1)){
d[i][j]=d[i-1][j-1];
}else{
d[i][j]=1+Math.min(d[i-1][j-1], Math.min(d[i-1][j], d[i][j-1]));
}
}
}
return d[m][n];
}
public boolean isInterleave(String s1, String s2, String s3) {
if(s1.length()+s2.length()!=s3.length()){
return false;
}
int m=s1.length(),n=s2.length(),k=s3.length();
boolean[][] d=new boolean[m+1][n+1];
d[0][0]=true;
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
if(i==0&&j==0){
continue;
}
if(i-1>=0&&d[i-1][j]&&s1.charAt(i-1)==s3.charAt(i+j-1)){
d[i][j]=true;
}
if(j-1>=0&&d[i][j-1]&&s2.charAt(j-1)==s3.charAt(i+j-1)){
d[i][j]=true;
}
}
}
return d[m][n];
}
思考:如果是更一般的情况,s1和s2不是正好交替组合成s3,而是可能有多余的字符呢? public boolean isInterleave(String s1, String s2, String s3) {
if(s1.length()+s2.length()!=s3.length()){
return false;
}
//d[i][j]表示s1前i个字符和s2前j个能交替表示s3的前d[i][j]个字符
int m=s1.length(),n=s2.length(),k=s3.length();
int[][] d=new int[m+1][n+1];
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
int max=0;
if((i>0&&d[i-1][j]>=k)||(j>0&&d[i][j-1]>=k))
max=k;
if(i>0&&s1.charAt(i-1)==s3.charAt(d[i-1][j])){
max=Math.max(max, d[i-1][j]+1);
}
if(j>0&&s2.charAt(j-1)==s3.charAt(d[i][j-1])){
max=Math.max(max, d[i][j-1]+1);
}
d[i][j]=max;
}
}
return d[m][n]==k;
}
最小化数组乘积
public int minMultiply(int[] a,int [] b){
int m=a.length,n=b.length;
int[][] d=new int[m+1][n+1];
for(int i=1;i<=m;i++){
for(int j=i;j<=n;j++){
if(j==i){
d[i][j]=d[i-1][j-1]+a[i-1]*b[j-1];
}else{
d[i][j]=Math.min(d[i-1][j-1]+a[i-1]*b[j-1], d[i][j-1]);
}
}
}
return d[m][n];
}
public int minPathSum(int[][] grid) {
if(grid.length==0||grid[0].length==0) return 0;
int m=grid.length,n=grid[0].length;
int[][] d=new int[m][n];
int sum=0;
for(int i=0;i<m;i++){
sum+=grid[i][0];
d[i][0]=sum;
}
sum=0;
for(int j=0;j<n;j++){
sum+=grid[0][j];
d[0][j]=sum;
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
d[i][j]=grid[i][j]+Math.min(d[i][j-1], d[i-1][j]);
}
}
return d[m-1][n-1];
}
public int uniquePaths(int m, int n) {
if(m==0||n==0)
return 1;
int[][] d=new int[m][n];
for(int i=0;i<m;i++) d[i][0]=1;
for(int j=0;j<n;j++) d[0][j]=1;
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
d[i][j]=d[i-1][j]+d[i][j-1];
}
}
return d[m-1][n-1];
}
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
if(obstacleGrid.length==0&&obstacleGrid[0].length==0){
return 0;
}
if(obstacleGrid[0].length==0){
return 1;
}
int m=obstacleGrid.length,n=obstacleGrid[0].length;
int[][] d=new int[m][n];
d[0][0]=(obstacleGrid[0][0]==1)?0:1;
for(int i=1;i<m;i++){
if(obstacleGrid[i][0]==1){
d[i][0]=0;
}else{
d[i][0]=d[i-1][0];
}
}
for(int j=1;j<n;j++){
if(obstacleGrid[0][j]==1){
d[0][j]=0;
}else{
d[0][j]=d[0][j-1];
}
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if(obstacleGrid[i][j]==1){
d[i][j]=0;
}else{
d[i][j]=d[i][j-1]+d[i-1][j];
}
}
}
return d[m-1][n-1];
}
public int minimumTotal(List<List<Integer>> triangle) {
if(triangle.size()==0||triangle.get(0).size()==0){
return 0;
}
int m=triangle.size(),n=m;
int[][] d=new int[m][n];
int sum=0;
for(int i=0;i<m;i++){
sum+=triangle.get(i).get(0);
d[i][0]=sum;
}
for(int i=1;i<m;i++){
for(int j=1;j<=i;j++){
if(i==j){
d[i][j]=triangle.get(i).get(j)+d[i-1][j-1];
}else{
d[i][j]=triangle.get(i).get(j)+Math.min(d[i-1][j], d[i-1][j-1]);
}
}
}
int min=Integer.MAX_VALUE;
for(int j=0;j<n;j++){
min=Math.min(min, d[m-1][j]);
}
return min;
}
改进
public int minimumTotal(List<List<Integer>> triangle) {
if(triangle.size()==0||triangle.get(0).size()==0){
return 0;
}
int n=triangle.size();
int[] d=new int[n];
int[] pre=new int[n];
d[0]=triangle.get(0).get(0);
for(int row=1;row<n;row++){
int[] t=d;d=pre;pre=t;
for(int col=0;col<=row;col++){
if(col==0){
d[0]=pre[0]+triangle.get(row).get(0);
}else{
if(row==col){
d[col]=triangle.get(row).get(col)+pre[col-1];
}else{
d[col]=triangle.get(row).get(col)+Math.min(pre[col-1],pre[col]);
}
}
}
}
int min=Integer.MAX_VALUE;
for(int i=0;i<n;i++){
min=Math.min(min, d[i]);
}
return min;
}
public int numTrees(int n) {
if(n<=1)return 1;
int[] nums=new int[n+1];
nums[0]=1;nums[1]=1;
for(int i=2;i<=n;i++){
int sum=0;
for(int j=0;j<i;j++){
sum=sum+nums[j]*nums[i-j-1];
}
nums[i]=sum;
}
return nums[n];
}
public class Solution {
public int maximalRectangle(char[][] matrix) {
if(matrix.length==0||matrix[0].length==0){
return 0;
}
int m=matrix.length,n=matrix[0].length;
int[][] rowMax=new int[m+1][n+1];//在行上以matrix[row][col]结尾的连续1的个数
int[][] colMax=new int[m+1][n+1];//在列上以matrix[row][col]结尾的连续1的个数
int maxSize=0;
for(int row=1;row<=m;row++){//迭代计算
for(int col=1;col<=n;col++){
if(matrix[row-1][col-1]=='0'){
rowMax[row][col]=0;
colMax[row][col]=0;
}else{
rowMax[row][col]=rowMax[row][col-1]+1;
colMax[row][col]=colMax[row-1][col]+1;
}
}
}
//求解
for(int row=1;row<=m;row++){
for(int col=1;col<=n;col++){
if(matrix[row-1][col-1]=='0'){
continue;
}else{
int min=Integer.MAX_VALUE;
int colLength=colMax[row][col];//在列上以该元素结尾的连续1个数
int[] rowMin=new int[colLength];//以当前行结尾的连续i+1行最小的行长度
for(int i=0;i<colLength;i++){
min=Math.min(min, rowMax[row-i][col]);
rowMin[i]=min;
maxSize=Math.max(maxSize, rowMin[i]*(i+1));//计算面积,行数*
}
}
}
}
return maxSize;
}
}
public int longestConsecutive(int[] nums) {
if(nums.length==0) return 0;
Map<Integer,Boolean> markMap=new HashMap<Integer,Boolean>();
for(int i=0;i<nums.length;i++){
markMap.put(nums[i], true);
}
//<元素值,以该元素为结尾的连续序列长度>
Map<Integer,Integer> countMap=new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
if(countMap.get(nums[i])!=null){//已经处理过了
continue;
}else{
int count=1,num=nums[i]-1;
while(true){
if(markMap.get(num)==null){//元素不存在中断扩展
break;
}else{//元素存在
if(countMap.get(num)==null){//还未进行扩展,继续扩展
count++;num--;
}else{//num已经扩展过了,统计数量并中断扩展
count+=countMap.get(num);
break;
}
}
}
//保存扩展之后的结果
System.out.println(i+" "+count);
num=nums[i];
while(markMap.get(num)!=null){
if(countMap.get(num)!=null){//已经保存过扩展结果了,中断保存过程
break;
}else{//保存扩展后的结果
countMap.put(num--, count--);
}
}
}
}
int max=Integer.MIN_VALUE;
for(int i=0;i<nums.length;i++){
max=Math.max(max, countMap.get(nums[i]));
}
return max;
}
public int longestConsecutive(int[] nums) {
if(nums.length==0) return 0;
Map<Integer,Integer> lengthMap=new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
lengthMap.put(nums[i], 0);
}
int max=Integer.MIN_VALUE;
for(int i=0;i<nums.length;i++){
solveLength(nums[i],lengthMap);
if(lengthMap.get(nums[i])>max){
max=lengthMap.get(nums[i]);
}
}
return max;
}
private void solveLength(int n,Map<Integer,Integer> lengthMap){
if(lengthMap.get(n).equals(0)){//还没有处理过
if(lengthMap.get(n-1)!=null){//n-1存在,先处理n-1的连续长度
solveLength(n-1,lengthMap);
lengthMap.put(n, lengthMap.get(n-1)+1);
}else{
lengthMap.put(n, 1);
}
}
}