算法_贪心算法篇
Posted 葡萄籽-June
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法_贪心算法篇相关的知识,希望对你有一定的参考价值。
LeetCode_跳跃游戏
本文通过跳跃游戏的几个算法例题总结一下相关贪心算法,开始学习的阶段,借鉴好的算法思路并学习,也巩固一下最近学习的算法。(后续遇到贪心算法会续更~~)
前言
贪心算法——在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,它所做出的仅仅是在某种意义上的局部最优解。
贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性(即某个状态以后的过程不会影响以前的状态,只与当前状态有关。)
贪心算法的基本思路:
- 建立数学模型来描述问题
- 把求解的问题分成若干个子问题
- 对每个子问题求解,得到子问题的局部最优解
- 把子问题的解局部最优解合成原来问题的一个解
该算法存在的问题:
- 不能保证求得的最后解是最佳的
- 不能用来求最大值或最小值的问题
- 只能求满足某些约束条件的可行解的范围
贪心策略适用的前提是:局部最优策略能导致产生全局最优解。
一、跳跃游戏I
=>题目:
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2: 输入:nums = [3,2,1,0,4]输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
提示:
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105
大致思路:
分析:
由题目得出,要想到达最后一个下标,得满足两个条件:
1、假设每个位置都能跳到,那么只需要遍历数组,看看有没有位置能直接通过这个位置上的数字跳到结尾。
比如[2,3,1,1,4],我们遍历数字,看看哪个位置可以跳到最后,可以发现第三个位置的数字是2,所以可以通过第三个位置跳到最后的下标,数组成立。
2、上述假设成立的还有个条件就是每个位置是否都能跳到。
比如[3,2,1,0,4],按照上面的逻辑,第三个位置可以跳跃1步到第4个位置,但第4个位置不可跳跃,因此不能到达最后位置。
public class CanJumpGames {
//方法1
public boolean canJump(int[] nums) {
//n为步数,初始为1
int step = 1;
//从倒数第2位开始检验
for (int i = nums.length - 2; i >= 0; i--) {
//nums[i]大于或等于step,说明从此位置可以跳到最后一位
if (nums[i] >= step) {
step = 1;
} else {
//若nums[i]小于step,说明从此位置不能跳到最后一位,则step++,向前遍历查看其它位置是否能跳到最后一位
step++;
}
//遍历到位置0时,且跳跃的步数大于1,则返回false
if (i == 0 && step > 1) {
return false;
}
}
return true;
}
//方法2
public boolean canJump(int[] nums) {
//能到达的最大位置k
int k =0;
//获取数组长度
int len = nums.length;
//遍历数组
for(int i=0;i<len ;i++){
if(k<i) return false;
k=Math.max(k,i+nums[i]);
if (k >= len -1) {
//减少循环次数
return true;
}
}
return false;
}
}
二、跳跃游戏II
题目:
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入: nums = [2,3,0,1,4]
输出: 2
大致思路:
简单分析:[2,3,1,1,4]中,第一步数字是2,可以跳到数字是3和数字是1的两个地方,贪心选择大的,即选择跳到3那步;从3即下标1的位置可以跳到数字分别是1,1,4的三个地方,贪心选择4;而4即为最后一位,最终结果是需要跳2次。
public class CanJump2 {
// 贪心
public int jump(int[] nums) {
// 若数组长度为1,则不需要跳跃
if(nums.length == 1) {return 0;}
// 每次跳跃的开始位置
int reach = 0;
// 跳跃后到达的位置的数值,开始在下标为0的位置
int nextreach = nums[0];
// 跳跃步数
int step = 0;
for(int i = 0;i<nums.length;i++){
// 取目前最大的跳跃
nextreach = Math.max(i+nums[i],nextreach);
if(nextreach >= nums.length-1) {return (step+1);}
if(i == reach){
// 当nextreach下标的值不能跳跃到最后一位时,step++且让reach到达nextreach下标的位置
step++;
reach = nextreach;
}
}
return step;
}
//动态规划 较占用内存,消耗时间较长
public int jump01(int[] nums) {
int[] dp = new int[nums.length];//dp[i] 为到达 i 位置的最小跳数
dp[0] = 0;//到达下标0的最小跳数是0
for (int i = 1; i < nums.length; i++) {
dp[i] = Integer.MAX_VALUE;//先把到达此步数的设置为无限大,作为下面取最小步数时不会有误的临界值
for (int j = 0; j < i; j++) {
if (i - j <= nums[j]) {
// 选择 i 之前的能跳到 i 的所有位置 j 中, dp[j] 值最小的位置 j 作为上一步要跳到 i 的位置
dp[i] = Math.min(dp[i], dp[j] + 1);//dp[i]=dp[j]+1 ,从 j 跳到 i ,步数加1
}
}
}
// dp[nums.length-1] 即到达最后一个位置的最小跳数
return dp[nums.length - 1];
}
}
以上是关于算法_贪心算法篇的主要内容,如果未能解决你的问题,请参考以下文章