1. 程式人生 > >Leetcode-貪心演算法

Leetcode-貪心演算法

貪心演算法(又稱貪婪演算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。 也就是說,不從整體最優上加以考慮,他所做出的是在某種意義上的區域性最優解。


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;
    }
}