算法总结

114 阅读3分钟

动态规划

贪心算法

每一步都寻求最优解,最终结果也是最优解.

跳跃游戏

[跳跃游戏,采用贪心算法](55. 跳跃游戏 - 力扣(LeetCode))

function getJump(arr){
        //最远能到达的距离
           let maxReach=0
          //将maxReach暂时存储在end
           let end=0
           //步长
           let step=0
           for(let i=0;i<arr.length-1;i++){
               maxReach=Math.max(maxReach,i+arr[i])
               if(i==end){
                   step++
                   end=maxReach
               }
           }
           return step++
        }`

user.jpg

跳跃游戏2

[采用贪心算法](45. 跳跃游戏 II - 力扣(LeetCode))

`var canJump = function(nums) {
let step=0;
let end=0;
let maxReach=0
for(let i=0;i<nums.length-1;i++){
    maxReach=Math.max(maxReach,i+nums[i])
    if(i==end){
        end=maxReach
    }
}
if(end>=nums.length-1){
    return true
}
else{
    return false
}
};`

总结

两道题都采用了贪心算法,每一步都找一个最远到达距离,然后通过max方法来确定下一步到达的最远距离。 注意:下一步的最远距离不是由上一步的最远距离来决定的,所以将最远距离存储在end中很有必要

动态规划

1.(53. 最大子数组和 - 力扣(LeetCode))
image.png 总结:
动态规划题的步骤:
1.定义状态,找到最理想的子状态,如本题最理想的子状态就是以数组中某个数结尾的子数组。
第一个子状态:以-2结尾的子数组
第二个子状态:以1结尾的子数组
第三个子状态:以-3结尾的子数组
......
每个子状态的最优解(除了第一个是他本身nums[0])都是前一个的最优解+本身nums[i],或者就是本身nums[i]

2.写转移方程式
子状态确定好了后,就该写转移方程了。

//如果前面那个状态为负数,就抛弃那个状态,否则,就加上。因为题目求的是最大的和。
//所有阻止成为最大和的状态都应该被抛弃
if(dp[i-1)<0){
dp[i]=nums[i]
}
if(dp[i-1)>0){
dp[i]=dp[i-1]+nums[i]
}


完整代码如下:

var maxSubArray = function(nums) {
//定义状态
//定义子问题为以nums[i]结尾的子数组

//状态转移方程
//dp[i]=dp[i-1]+nums[i]
//方法一
// //初始化
// let dp=[]
// let max=nums[0]
// for(let i=0;i<nums.length;i++){
//     dp[0]=nums[0]
//     if(dp[i-1]<0){
//          dp[i]=nums[i]
//     }
//      else{
//          dp[i]=dp[i-1]+nums[i]
//      }
//这一步是上面if-else的优化
//     //dp[i]=Math.max(dp[i-1]+nums[i],nums[i])
//将最大的子状态存储起来
//     if(dp[i]>max){
//         max=dp[i]
//     }
// }
//输出
// return max


//方法二:节省空间。不用新建数组,直接将结果存在数组里面
let max=nums[0]
if(nums.length==1){
    return nums[0]
}
for(let i=1;i<=nums.length-1;i++){
    
    nums[i]+=Math.max(nums[i-1],0)
   max=Math.max(max,nums[i])
}
return max
};

####################################

2.(62. 不同路径 - 力扣(LeetCode)) 题目:
image.png

分析:
1.定义子状态:到达终点的过程,每一步就是一个子状态。除了边界外,每个格子的路径总和都等于左格子路径总和加上右格子路径总和。
2.状态转移方程:dp[i][j]=dp[i-1][j]+dp[i][j-1]
3.边界问题:不难看出,矩阵的第一排和第一列就是这道题的边界问题。因为机器人只能向下向右,所以矩阵的第一排和第一列都是一个路径可以到达。所以dp[i][j]=1

代码:

var uniquePaths = function(m, n) {
let f=new Array(m).fill(0).map(()=>{
    return new Array(n).fill(0)
})
//定义状态
//每一步的路径等于x-1,y这步的路径+x,y-1这步的路径
//当x=0||y=0时,路径都只有一种,因为机器人只能向右向下走


//状态转移方程
//dp[x][y]=dp[x-1][y]+dp[x][y-1]


//初始化
for(let x=0;x<m;x++){
    for(let y=0;y<n;y++){
        if(x==0||y==0){
            f[x][y]=1
        }
        else{
            f[x][y]=f[x-1][y]+f[x][y-1]
        }
    }
}
return f[m-1][n-1]

};

3.64. 最小路径和 - 力扣(LeetCode)

image.png

分析:与2大同小异,还是最优子状态,边界问题和状态转移方程的推导
代码:

var minPathSum = function(dp) {
//到目的地路径和最小,则到最近两个位置的路径和最小
//dp[x][y]+=Math.min(dp[x-1][y],dp[x][y-1])


//临界条件
for(let x=0;x<dp.length;x++){
    for(let y=0;y<dp[0].length;y++){
        if(x==0&&y==0){
            continue;
        }
        if(x==0){
            dp[x][y]+=dp[x][y-1]
        }
        if(y==0){
            dp[x][y]+=dp[x-1][y]
        }
        if(x!=0&&y!=0){
            dp[x][y]+=Math.min(dp[x-1][y],dp[x][y-1])
        }
    }
}
return dp[dp.length-1][dp[0].length-1]
//关键在于找到每一步的最小值,只要求输出总和

};

总结

1.动态规划的题,一般会出现“连续”,“最”这样的字。
2.解动态规划的题,一般步骤是定义最优子状态,写状态转移方程,边界问题。边界问题最容易被忽视,要考虑到多重条件!!!