LeetCode 貪心


基礎部分

455. 分發餅干

簡單

假設你是一位很棒的家長,想要給你的孩子們一些小餅干。但是,每個孩子最多只能給一塊餅干。對每個孩子 i ,都有一個胃口值 gi ,這是能讓孩子們滿足胃口的餅干的最小尺寸;並且每塊餅干 j ,都有一個尺寸 sj 。如果 sj >= gi ,我們可以將這個餅干 j 分配給孩子 i ,這個孩子會得到滿足。你的目標是盡可能滿足越多數量的孩子,並輸出這個最大數值。

注意:

你可以假設胃口值為正。
一個小朋友最多只能擁有一塊餅干。

示例 1:

輸入: [1,2,3], [1,1]

輸出: 1

解釋: 
你有三個孩子和兩塊小餅干,3個孩子的胃口值分別是:1,2,3。
雖然你有兩塊小餅干,由於他們的尺寸都是1,你只能讓胃口值是1的孩子滿足。
所以你應該輸出1。

示例 2:

輸入: [1,2], [1,2,3]

輸出: 2

解釋: 
你有兩個孩子和三塊小餅干,2個孩子的胃口值分別是1,2。
你擁有的餅干數量和尺寸都足以讓所有孩子滿足。
所以你應該輸出2.
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int res = 0;
        int i = g.length-1;
        int j = s.length-1;
        while (i >= 0 && j >=0){
            if (s[j] >= g[i]){ //滿足了拿去吃
                res++;
                j--;
            }
            i--;
        }
        return res;
    }
}

435. 無重疊區間

中等

給定一個區間的集合,找到需要移除區間的最小數量,使剩余區間互不重疊。

注意:

  1. 可以認為區間的終點總是大於它的起點。
  2. 區間 [1,2] 和 [2,3] 的邊界相互“接觸”,但沒有相互重疊。

示例 1:

輸入: [ [1,2], [2,3], [3,4], [1,3] ]

輸出: 1

解釋: 移除 [1,3] 后,剩下的區間沒有重疊。

示例 2:

輸入: [ [1,2], [1,2], [1,2] ]

輸出: 2

解釋: 你需要移除兩個 [1,2] 來使剩下的區間沒有重疊。

示例 3:

輸入: [ [1,2], [2,3] ]

輸出: 0

解釋: 你不需要移除任何區間,因為它們已經是無重疊的了。
class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        int len = intervals.length;
        if (len < 2) return 0;
        Arrays.sort(intervals,(a,b)->(a[0]-b[0])); //以第一位升序排列
        List<int[]> list = new LinkedList<>();
        list.add(intervals[0]);
        for (int i = 0; i < intervals.length; i++) {
            int tail = list.get(list.size()-1)[1];
            if (tail > intervals[i][0]){
                if (tail > intervals[i][1]) //留尾巴短的,后邊不易重疊
                    list.get(list.size()-1)[1] = intervals[i][1];
            }else list.add(intervals[i]);
        }
        return len - list.size();
    }
}

452. 用最少數量的箭引爆氣球

中等

在二維空間中有許多球形的氣球。對於每個氣球,提供的輸入是水平方向上,氣球直徑的開始和結束坐標。由於它是水平的,所以y坐標並不重要,因此只要知道開始和結束的x坐標就足夠了。開始坐標總是小於結束坐標。平面內最多存在104個氣球。

一支弓箭可以沿着x軸從不同點完全垂直地射出。在坐標x處射出一支箭,若有一個氣球的直徑的開始和結束坐標為 xstart,xend, 且滿足 xstart ≤ x ≤ xend,則該氣球會被引爆。可以射出的弓箭的數量沒有限制。 弓箭一旦被射出之后,可以無限地前進。我們想找到使得所有氣球全部被引爆,所需的弓箭的最小數量。

Example:

輸入:
[[10,16], [2,8], [1,6], [7,12]]

輸出:
2

解釋:
對於該樣例,我們可以在x = 6(射爆[2,8],[1,6]兩個氣球)和 x = 11(射爆另外兩個氣球)。
class Solution {
    public int findMinArrowShots(int[][] points) {
        if (points.length < 2) return points.length;
        Arrays.sort(points,(a,b)->(a[0]-b[0]));
        int res = 0;
        int i = 0;
        while (i < points.length){
            res++;
            int last = points[i][1];
            do {
                i++;
                if (i < points.length && points[i][1] < last) 
                    last = points[i][1]; //整體的尾巴往前才能都中
            }while (i < points.length && points[i][0] <= last);
        }
        return res;
    }
}

406. 根據身高重建隊列

中等

假設有打亂順序的一群人站成一個隊列。 每個人由一個整數對(h, k)表示,其中h是這個人的身高,k是排在這個人前面且身高大於或等於h的人數。 編寫一個算法來重建這個隊列。

注意:
總人數少於1100人。

示例

輸入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

輸出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
class Solution {
    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people,(a,b)->a[1]-b[1]); //第二位正序
        Arrays.sort(people,(a,b)->b[0]-a[0]); //第一位倒序
        for (int i = 0; i < people.length; i++)
            if (people[i][1] != i) { //people[i][1]:該去的位置
                int j = i;
                int[] tmp = people[j];
                while (j != tmp[1]) { //tmp很關鍵,因為i和j都變化了,必須tmp
                    people[j] = people[j - 1];
                    j--;
                }
                people[j] = tmp;
            }
        return people;
    }
}

121. 買賣股票的最佳時機

簡單

給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

如果你最多只允許完成一筆交易(即買入和賣出一支股票一次),設計一個算法來計算你所能獲取的最大利潤。

注意:你不能在買入股票前賣出股票。

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
     注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格;同時,你不能在買入前賣出股票。

示例 2:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。
class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        int prof = 0;
        for (int i = 1; i < prices.length; i++) {
            int minus = prices[i] - prices[i-1];
            prof += minus;
            if (prof < 0) prof = 0; //負收益還不如不買
            else if (prof > res) res = prof;
        }
        return res;
    }
}

122. 買賣股票的最佳時機 II

簡單

給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

設計一個算法來計算你所能獲取的最大利潤。你可以盡可能地完成更多的交易(多次買賣一支股票)。

注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

示例 1

輸入: [7,1,5,3,6,4]
輸出: 7
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 3 天(股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
     隨后,在第 4 天(股票價格 = 3)的時候買入,在第 5 天(股票價格 = 6)的時候賣出, 這筆交易所能獲得利潤 = 6-3 = 3 。

示例 2:

輸入: [1,2,3,4,5]
輸出: 4
解釋: 在第 1 天(股票價格 = 1)的時候買入,在第 5 天 (股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接連購買股票,之后再將它們賣出。
     因為這樣屬於同時參與了多筆交易,你必須在再次購買前出售掉之前的股票。

示例 3:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。

提示:

  • 1 <= prices.length <= 3 * 10 ^ 4
  • 0 <= prices[i] <= 10 ^ 4
class Solution {
    public int maxProfit(int[] prices) {
        int prof = 0;
        for (int i = 1; i < prices.length; i++) {
            int minus = prices[i] - prices[i-1];
            prof += minus > 0 ? minus : 0;
        }
        return prof;
    }
}

605. 種花問題

簡單

假設你有一個很長的花壇,一部分地塊種植了花,另一部分卻沒有。可是,花卉不能種植在相鄰的地塊上,它們會爭奪水源,兩者都會死去。

給定一個花壇(表示為一個數組包含0和1,其中0表示沒種植花,1表示種植了花),和一個數 n 。能否在不打破種植規則的情況下種入 n 朵花?能則返回True,不能則返回False。

示例 1:

輸入: flowerbed = [1,0,0,0,1], n = 1
輸出: True

示例 2:

輸入: flowerbed = [1,0,0,0,1], n = 2
輸出: False

注意:

  1. 數組內已種好的花不會違反種植規則。
  2. 輸入的數組長度范圍為 [1, 20000]。
  3. n 是非負整數,且不會超過輸入數組的大小。
class Solution {
    public boolean canPlaceFlowers(int[] flowerbed, int n) {
        int k = 0;
        for (int i = 0; i < flowerbed.length && k<n; i++) {
            if (flowerbed[i] == 0){
                if (i+1 == flowerbed.length || flowerbed[i+1] == 0){
                    flowerbed[i] = 1;
                    k++;
                    i++; //下一個地方肯定不能種,跳過
                }
            }else {
                i++; //下一個地方肯定不能種,跳過
            }
        }
        return k >= n;
    }
}

392. 判斷子序列

簡單

給定字符串 st ,判斷 s 是否為 t 的子序列。

你可以認為 st 中僅包含英文小寫字母。字符串 t 可能會很長(長度 ~= 500,000),而 s 是個短字符串(長度 <=100)。

字符串的一個子序列是原始字符串刪除一些(也可以不刪除)字符而不改變剩余字符相對位置形成的新字符串。(例如,"ace""abcde"的一個子序列,而"aec"不是)。

示例 1:
s = "abc", t = "ahbgdc"

返回 true.

示例 2:
s = "axc", t = "ahbgdc"

返回 false.

后續挑戰 :

如果有大量輸入的 S,稱作S1, S2, ... , Sk 其中 k >= 10億,你需要依次檢查它們是否為 T 的子序列。在這種情況下,你會怎樣改變代碼?

class Solution {
    public boolean isSubsequence(String s, String t) {
        char[] schars = s.toCharArray();
        char[] tchars = t.toCharArray();
        int i, j;
        for (i = 0,j = 0; i < schars.length; i++) {
            while (j < tchars.length && tchars[j] != schars[i]) j++;
            if (j >= tchars.length) break;
            j++;
        }
        return i == schars.length;
    }
}

665. 非遞減數列

簡單

給你一個長度為 n 的整數數組,請你判斷在 最多 改變 1 個元素的情況下,該數組能否變成一個非遞減數列。

我們是這樣定義一個非遞減數列的: 對於數組中所有的 i (0 <= i <= n-2),總滿足 nums[i] <= nums[i + 1]

示例 1:

輸入: nums = [4,2,3]
輸出: true
解釋: 你可以通過把第一個4變成1來使得它成為一個非遞減數列。

示例 2:

輸入: nums = [4,2,1]
輸出: false
解釋: 你不能在只改變一個元素的情況下將其變為非遞減數列。

說明:

  • 1 <= n <= 10 ^ 4
  • - 10 ^ 5 <= nums[i] <= 10 ^ 5
class Solution {
    public boolean checkPossibility(int[] nums) {
        int fix = 0;
        for (int i = 1; i < nums.length && fix <= 1; i++){
            if (nums[i] < nums[i-1]){
                fix++;
                if (i-2 >= 0 && nums[i]<nums[i-2]){ //突然有個數字小了
                    nums[i]=nums[i-1];
                }else { //前邊的數字小了
                    nums[i-1]=nums[i];
                }
            }
        }
        return fix <= 1;
    }
}

53. 最大子序和

簡單

給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。

示例:

輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,為 6。

進階:

如果你已經實現復雜度為 O(n) 的解法,嘗試使用更為精妙的分治法求解。

class Solution {
    public int maxSubArray(int[] nums) {
        int res = Integer.MIN_VALUE;
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            res = res > sum ? res : sum;
            sum = sum < 0 ? 0 : sum;
        }
        return res;
    }
}

763. 划分字母區間

中等

字符串 S 由小寫字母組成。我們要把這個字符串划分為盡可能多的片段,同一個字母只會出現在其中的一個片段。返回一個表示每個字符串片段的長度的列表。

示例 1:

輸入:S = "ababcbacadefegdehijhklij"
輸出:[9,7,8]
解釋:
划分結果為 "ababcbaca", "defegde", "hijhklij"。
每個字母最多出現在一個片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是錯誤的,因為划分的片段數較少。

提示:

  • S的長度在[1, 500]之間。
  • S只包含小寫字母 'a''z'
class Solution {
    public List<Integer> partitionLabels(String S) {
        char[] chars = S.toCharArray();
        Map<Character,Integer> map = new HashMap<>();
        for (int i = 0; i < chars.length; i++) {
            map.put(chars[i],i);
        }
        List<Integer> list = new LinkedList<>();
        int max = 0;
        int i = 0;
        int j = 0;
        while (j < chars.length){
            max = Math.max(max,map.get(chars[j]));
            if (j++ == max) {
                list.add(j-i);
                i = j;
            }
        }
        return list;
    }
}

頻率排序

135. 分發糖果

困難

老師想給孩子們分發糖果,有 N 個孩子站成了一條直線,老師會根據每個孩子的表現,預先給他們評分。

你需要按照以下要求,幫助老師給這些孩子分發糖果:

  • 每個孩子至少分配到 1 個糖果。
  • 相鄰的孩子中,評分高的孩子必須獲得更多的糖果。

那么這樣下來,老師至少需要准備多少顆糖果呢?

示例 1:

輸入: [1,0,2]
輸出: 5
解釋: 你可以分別給這三個孩子分發 2、1、2 顆糖果。

示例 2:

輸入: [1,2,2]
輸出: 4
解釋: 你可以分別給這三個孩子分發 1、2、1 顆糖果。
     第三個孩子只得到 1 顆糖果,這已滿足上述兩個條件。
class Solution {
    public int candy(int[] ratings) {
        int len = ratings.length;
        int[] res = new int[len];
        res[0] = 1;
        for (int i = 1; i < len; i++) {
            if (ratings[i] == ratings[i-1]) res[i] = 1;
            else if (ratings[i] > ratings[i-1]) res[i] = res[i-1]+1;
            else { //發現前面的孩子給少了
                res[i] = 1;
                int j = i;
                do {
                    j--;
                    if (res[j] <= res[j+1]){
                        res[j] = res[j+1] + 1;
                    }else {
                        break;
                    }
                }while (j > 0 && ratings[j] < ratings[j-1]);
            }
        }
        int candys = 0;
        for (int candy : res) candys += candy;
        return candys;
    }
}

316. 去除重復字母

困難

給你一個僅包含小寫字母的字符串,請你去除字符串中重復的字母,使得每個字母只出現一次。需保證返回結果的字典序最小(要求不能打亂其他字符的相對位置)。

示例 1:

輸入: "bcabc"
輸出: "abc"

示例 2:

輸入: "cbacdcbc"
輸出: "acdb"
public class Solution {
   public String removeDuplicateLetters(String s) {
       int[] cnt = new int[26];
       int pos = 0;
       for (int i = 0; i < s.length(); i++) //字母出現次數的字典
           cnt[s.charAt(i) - 'a']++;
       for (int i = 0; i < s.length(); i++) {
           if (s.charAt(i) < s.charAt(pos)) pos = i; //后邊的比pos的字典順序小
           if (--cnt[s.charAt(i) - 'a'] == 0) break; //字典里減沒了,pos可以放第一位
       }
       return s.length() == 0 ? "" : s.charAt(pos) + 
               removeDuplicateLetters(s.substring(pos + 1) //遞歸
               .replaceAll("" + s.charAt(pos), "") //這個字符統計完了,全部去掉
       ); 
   }
}

45. 跳躍游戲 II

困難

給定一個非負整數數組,你最初位於數組的第一個位置。

數組中的每個元素代表你在該位置可以跳躍的最大長度。

你的目標是使用最少的跳躍次數到達數組的最后一個位置。

示例:

輸入: [2,3,1,1,4]
輸出: 2
解釋: 跳到最后一個位置的最小跳躍數是 2。
     從下標為 0 跳到下標為 1 的位置,跳 1 步,然后跳 3 步到達數組的最后一個位置。

說明:

假設你總是可以到達數組的最后一個位置。

class Solution {
    public int jump(int[] nums) {
        int res = 0;
        for (int i = nums.length - 1; i > 0; ) {
            int pos = i - 1;
            for (int j = 0; j < i; j++)
                if (nums[j] >= i-j) pos = Math.min(pos,j);
            res++;
            i = pos;
        }
        return res;
    }
}

714. 買賣股票的最佳時機含手續費

中等

給定一個整數數組 prices,其中第 i 個元素代表了第 i 天的股票價格 ;非負整數 fee 代表了交易股票的手續費用。

你可以無限次地完成交易,但是你每筆交易都需要付手續費。如果你已經購買了一個股票,在賣出它之前你就不能再繼續購買股票了。

返回獲得利潤的最大值。

注意:這里的一筆交易指買入持有並賣出股票的整個過程,每筆交易你只需要為支付一次手續費。

示例 1:

輸入: prices = [1, 3, 2, 8, 4, 9], fee = 2
輸出: 8
解釋: 能夠達到的最大利潤:  
在此處買入 prices[0] = 1
在此處賣出 prices[3] = 8
在此處買入 prices[4] = 4
在此處賣出 prices[5] = 9
總利潤: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

注意:

  • 0 < prices.length <= 50000.
  • 0 < prices[i] < 50000.
  • 0 <= fee < 50000.
//未通過,不知道為什么
class Solution {
    public int maxProfit(int[] prices, int fee) {
        int sum = 0;
        int[] profs = new int[prices.length-1];
        for (int i = 0; i < profs.length; i++)
            profs[i] = prices[i+1] - prices[i];
        int prof = 0;
        int drop = 0;
        for (int i = 0; i < profs.length; i++) {
            if (prof == 0 && profs[i] < 0) continue;
            if (profs[i] < 0){
                drop += profs[i];
                if (drop <= -fee) { //賠fee塊錢,還不如交管理費賣了
                    if (prof > fee) sum += prof-fee;
                    drop = 0;
                    prof = 0;
                }
            }else {
                prof += drop + profs[i];
                drop = 0;
            }
        }
        sum += prof > fee ? prof - fee : 0; //最后一波沒加上
        return sum;
    }
}

402. 移掉K位數字

中等

給定一個以字符串表示的非負整數 num,移除這個數中的 k 位數字,使得剩下的數字最小。

注意:

  • num 的長度小於 10002 且 ≥ k。
  • num 不會包含任何前導零。

示例 1 :

輸入: num = "1432219", k = 3
輸出: "1219"
解釋: 移除掉三個數字 4, 3, 和 2 形成一個新的最小的數字 1219。

示例 2 :

輸入: num = "10200", k = 1
輸出: "200"
解釋: 移掉首位的 1 剩下的數字為 200. 注意輸出不能有任何前導零。

示例 3 :

輸入: num = "10", k = 2
輸出: "0"
解釋: 從原數字移除所有的數字,剩余為空就是0。
class Solution {
    public String removeKdigits(String num, int k) {
        LinkedList<Character> stack = new LinkedList<>();

        for(char digit : num.toCharArray()) {
            while(!stack.isEmpty() && k > 0 && stack.peekLast() > digit) {
                //stack不為空,k大於0,前面的數大於后面的數
                stack.removeLast(); //去掉前面的數
                k -= 1; //去掉一次少一次機會
            }
            stack.addLast(digit);
        }
        
        for(int i=0; i<k; ++i) stack.removeLast(); //還有機會沒用完
        
        StringBuilder res = new StringBuilder();
        while (stack.peekFirst() == '0') stack.pollFirst();
        for (Character c : stack) res.append(c);

        return res.length() > 0 ? res.toString() : "0";
    }
}

1029. 兩地調度

簡單

公司計划面試 2N 人。第 i 人飛往 A 市的費用為 costs[i][0],飛往 B 市的費用為 costs[i][1]

返回將每個人都飛到某座城市的最低費用,要求每個城市都有 N 人抵達

示例:

輸入:[[10,20],[30,200],[400,50],[30,20]]
輸出:110
解釋:
第一個人去 A 市,費用為 10。
第二個人去 A 市,費用為 30。
第三個人去 B 市,費用為 50。
第四個人去 B 市,費用為 20。

最低總費用為 10 + 30 + 50 + 20 = 110,每個城市都有一半的人在面試。

提示:

  1. 1 <= costs.length <= 100
  2. costs.length 為偶數
  3. 1 <= costs[i][0], costs[i][1] <= 1000
class Solution {
    public int twoCitySchedCost(int[][] costs) {
        int potA = costs.length/2;
        int potB = costs.length/2;
        Arrays.sort(costs,(x, y)->Math.abs(y[0]-y[1])-Math.abs(x[0]-x[1])); //相差越多權重越高,,權重高的人先挑
        int res = 0;
        for (int i = 0; i < costs.length; i++) {
            int a = costs[i][0];
            int b = costs[i][1];
            if (a < b) {
                if (potA-- > 0) res += a;
                else res += b;
            } else {
                if (potB-- > 0) res += b;
                else res += a;
            }
        }
        return res;
    }
}
//官方的,(A地-B地)的升序,這么簡單嗎,憑什么
class Solution {
    public int twoCitySchedCost(int[][] costs) {
        Arrays.sort(costs,(a,b)->a[0]-a[1]-b[0]+b[1]);
        int total = 0;
        int n = costs.length / 2;
        for (int i = 0; i < n; ++i) total += costs[i][0] + costs[i + n][1];
        return total;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM