Leetcode-贪心算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gaoruowen1/article/details/83273952

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。 也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。


455. 分发饼干

题目链接
在这里插入图片描述

思路

这道题目给了我们两组array,一组代表小朋友的胃口值,另一组代表曲奇饼干的大小。
我们要分发曲奇给尽可能多的小朋友,并且曲奇饼干的大小要满足小朋友的胃口。
所以,最好给孩子们分配与他胃口最接近的饼干
不能把大的曲奇去满足很小胃口的小朋友,除非没有选择。尽可能的去把小曲奇发给小胃口的小朋友。

关键点:把两个array 重新排列,从小到大

方法1:10ms 100%

对两个数组进行排序并使用两个指针。O(nlogn)

// 10ms
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        
        int pointG = 0;
        int pointS = 0;
        
        while (pointG < g.length && pointS < s.length) {
            if (g[pointG] <= s[pointS]) {
                pointG++;
                pointS++;
            } 
            else {
                pointS++;
            }
        }
        
        return pointG;
    }
}

方法2:16ms 38%

常规循环两个数组遍历,无指针,效率慢

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        int count=0;
        Arrays.sort(g);
        Arrays.sort(s);
        int j=0;
        for(int i=0; i<g.length; i++){
            while(j<s.length && s[j]<g[i]) j++;
            if(j==s.length) break;
            count++;
            j++;
        }
        return count;
    }
}

55. Jump Game (M)

题目链接

给定一个非负整数数组,数组中的每个元素表示该位置的最大跳跃长度。
从数组的第一个索引(下标为0)开始,确定是否能够跳到最后一个索引处。

Example 1:

Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.

思路

采用贪心的思路,采用farest变量表示到目前为止能跳到的最远距离,即为全局最优解。当遍历到 i 的时候,局部最优解为 A[i]+i,表示从当前一步出发能跳到的最远距离。
因此,此时的全局最优解即为 farest 和 A[i]+i 的最大值:farest = Math.max(farest, A[i] + i)。

关键点:“局部最优和全局最优解法”。全局最优为 globalMax=Math.max(globalMax, localMax)

public boolean canJump(int[] A) {
    int n = A.length;
    int farest = 0;
    
    for(int i = 0; i < n; i++){
        if(farest < i) return false;
        farest = Math.max(i + A[i], farest);
    }
    return true;
}

45. Jump Game II (H)

题目链接
给定一个非负整数数组,数组中的每个元素表示该位置的最大跳跃长度。
从数组的第一个索引(下标为0)开始,需要我们以最小跳跃次数到达最后一个索引。

Example:

Input: [2,3,1,1,4] Output: 2
Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index.

Note: You can assume that you can always reach the last index.

思路

这道题需要求的是最少的步数
因此需要添加step变量记录最少步数。至于什么时候step需要加1?答案是当前的 i 超过了前一step的最远位置。所以引入last变量记录上一步step能到达的最远位置。
当遍历到 i 的时候,如果 i == last(即上一step能到达的最远位置),说明步数需要加1(step++),此时仍需要更新last为当前最远位置farest。

关键点:记录最少步数step、判断何时step++

public int jump(int[] A) {
	int n = A.length;
    int step = 0, last = 0, farest = 0;
    
    for(int i = 0; i < n-1; i++) {
        farest = Math.max(farest, i+A[i]);
        if(farest >= n-1) return step+1;    //skip scanning when find a way with minimum jumps
        if(i == last) {
            step++;
            last = farest;
        } 
    }
    return step ;
}

122. Best Time to Buy and Sell Stock II

题目链接

假设您有一个数组, i t h i^{th} 元素是第 i 天股票的价格。
设计算法以找到最大利润。 您可以根据需要完成尽可能多的交易(即,多次进行买入卖出)。
注意:您不得同时进行多笔交易(即,您必须在再次购买之前卖出股票)。

Example 1:

Input: [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4. Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.

Example 2:

Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4. Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again.

Example 3:

Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

思路

贪心算法,在每一小段上升序列中最大差值累加得到结果。就是说在股票价格处于上升期的时候,在最低点买入,在最高点卖出。

class Solution {
    public int maxProfit(int[] prices) {
        int pro = 0;
        for (int i = 1; i < prices.length; i++){
            if (prices[i]> prices[i-1])
                pro += prices[i] - prices[i-1];
        }
        return pro;
    }
}

134. Gas Station (M)

题目链接

沿着环形路线有N个加油站,在加油站 i 的汽油数是 gas[i]。
你有一辆带有无限油箱的汽车,从加油站i 到下一加油站 (i+1) 需要消耗cost[i]汽油。 您使用空罐从其中任一个加油站启程。如果您可以顺时针方向绕环形一次,则返回起始加油站的索引,否则返回-1。

注意: 如果存在解决方案,则保证是唯一的。

Example 1:

Input: gas = [1,2,3,4,5]; cost = [3,4,5,1,2]
Output: 3
Explanation: Start at station 3 (index 3) and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 4. Your tank = 4 - 1 + 5 = 8
Travel to station 0. Your tank = 8 - 2 + 1 = 7
Travel to station 1. Your tank = 7 - 3 + 2 = 6
Travel to station 2. Your tank = 6 - 4 + 3 = 5
Travel to station 3. The cost is 5. Your gas is just enough to travel back to station 3. Therefore, return 3 as the starting index.

Example 2:

Input: gas = [2,3,4] cost = [3,4,3]
Output: -1
Explanation: You can’t start at station 0 or 1, as there is not enough gas to travel to the next station. Let’s start at station 2 and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 0. Your tank = 4 - 3 + 2 = 3
Travel to station 1. Your tank = 3 - 3 + 3 = 3
You cannot travel back to station 2, as it requires 4 unit of gas but you only have 3. Therefore, you can’t travel around the circuit once no matter where you start.

思路

首先先要明白:如果一个数组的总和非负,那么一定可以找到一个起始位置,从他开始绕数组一圈,累加和一直都是非负的。

如果从 i 出发,那么 gas[i]- cost[i] = dif[i] 一定是大于0的。
如果 dif[i] + dif[i+1] +……+ dif[k-1]>=0,而 dif[i] + dif[i+1] +……+ dif[k-1] + dif[k]<0,此时需要重新设置开始点,那么需要从i+1开始吗?从i+1开始肯定不可能到达k点(dif[i]肯定大于0,所以少加了一个正数)。
那么需要设置开始点为 k+1

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int startpos = 0;  // 记录访问的起始点
        int totaldif = 0;  // 从0位置开始,加的气和消耗的气的总差值
        int startdif = 0;  // 从start位置开始,加的气和消耗的气的总差值
        
        for (int i = 0; i < gas.length; i++){
            int dif = gas[i] - cost[i];
            totaldif += dif;
            startdif += dif;
            if (startdif < 0){    // 走到i站是负数,说明走不到i站;也不能选取起始站到i站中的任何一站
                startdif = 0;
                startpos = i + 1;   // 直接选择下一站作为起始点
            }
        }
        return totaldif >= 0 ? startpos : -1;
    }
}

860. Lemonade Change

题目链接

在柠檬水摊上,每个柠檬水的价格为5美元。
客户站在队列中向您购买,并一次订购一个(按照账单指定的顺序)。
每位顾客只需购买一瓶柠檬水,并以5美元,10美元或20美元的钞票付款。 您必须找给每个客户正确的零钱。

请注意,您最初手头没有任何零钱。当且仅当您能为每位客户提供正确的零钱时,才返回true。

Example 1:

Input: [5,5,5,10,20]
Output: true
Explanation: From the first 3 customers, we collect three $5 bills in order.
From the fourth customer, we collect a $10 bill and give back a $5.
From the fifth customer, we give a $10 bill and a $5 bill.
Since all customers got correct change, we output true.

Example 2:

Input: [5,5,10,10,20]
Output: false
Explanation: From the first two customers in order, we collect two $5 bills.
For the next two customers in order, we collect a $10 bill and give back a $5 bill.
For the last customer, we can’t give change of $15 back because we only have two $10 bills.
Since not every customer received correct change, the answer is false.

class Solution {
    public boolean lemonadeChange(int[] bills) {
        int five = 0, ten = 0;
        for (int i : bills) {
            if (i == 5) five++;
            else if (i == 10) {five--; ten++;}
            else if (ten > 0) {ten--; five--;}
            else five -= 3;
            if (five < 0) return false;
        }
        return true;
    }
}


猜你喜欢

转载自blog.csdn.net/gaoruowen1/article/details/83273952
今日推荐