LeetCode & 剑指offer 经典题目总结——贪心算法

目录

  • 1.加油站
  • 2.跳跃游戏
  • 3.连续子数组的最大和

1.加油站

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:

  • 如果题目有解,该答案即为唯一答案。
  • 输入数组均为非空数组,且长度相同。
  • 输入数组中的元素均为非负数。

示例 1:

输入: gas = [1,2,3,4,5] cost = [3,4,5,1,2]

输出: 3

解释: 从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。

示例 2:

输入:
gas = [2,3,4]
cost = [3,4,3]
输出: -1
解释: 你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

解法:
我们首先要知道能走完整个环的前提是gas的总量要大于cost的总量,这样才会有起点的存在。假设开始设置起点start = 0, 并从这里出发,如果当前的gas值大于cost值,就可以继续前进,此时到下一个站点,剩余的gas加上当前的gas再减去cost,看是否大于0,若大于0,则继续前进。当到达某一站点时,若这个值小于0了,则说明从起点到这个点中间的任何一个点都不能作为起点,则把起点设为下一个点,继续遍历。当遍历完整个环时,当前保存的起点即为所求。

    public int canCompleteCircuit(int[] gas, int[] cost) {
        int sum=0;
        int total=0;
        int start=0;
        for(int i=0;i<gas.length;i++){
            total+=gas[i]-cost[i];
            sum+=gas[i]-cost[i];
            if(sum<0){
                start=i+1;
                sum=0;
            }
        }
        return total>=0 ? start:-1;
    }

2.跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。

示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

解法一:

public class Solution {
    public boolean canJump(int[] A) {
        if(A.length<=1) return true;
        //从当前位置可达终点的最小距离
        int n=1;
        for(int i=A.length-2;i>=1;i--){
            if(A[i]>=n){
                n=1;
            }else{
                n++;
            }
        }
        if(A[0]>=n){
            return true;
        }else{
            return false;
        }
    }
}

解法二:

     public boolean canJump(int[] A) {
         //从起点能到达的最远位置
         int maxReach=0;
         for(int i=0;i<A.length && i<=maxReach;i++){
             maxReach=Math.max(maxReach,i+A[i]);
         }
         if(maxReach>=A.length-1){
             return true;
         }else{
             return false;
         }
     }

3.连续子数组的最大和

给定一个整数数组,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

解法:
从头开始累加元素,当tempSum的值为正时,为后面带来正收益,继续加上下一个元素;为负时为后面带来负收益,应舍弃。maxSum记录tempSum最大值。

public class Solution {
    public int maxSubArray(int[] A) {
        int maxSum=A[0];
        int tempSum=0;
        for(int i=0;i<A.length;i++){
            if(tempSum<0){
                tempSum=0;
            }
            tempSum+=A[i];
            maxSum=Math.max(tempSum,maxSum);
        }
        return maxSum;
    }
}

你可能感兴趣的:(数据结构与算法)