1. 程式人生 > 其它 >[leetcode刷題]——貪心思想

[leetcode刷題]——貪心思想

此篇部落格主要記錄力扣中的貪心思想。

一、分配餅乾

455.分發餅乾 easy 2021-06-10

假設你是一位很棒的家長,想要給你的孩子們一些小餅乾。但是,每個孩子最多隻能給一塊餅乾。

對每個孩子 i,都有一個胃口值g[i],這是能讓孩子們滿足胃口的餅乾的最小尺寸;並且每塊餅乾 j,都有一個尺寸 s[j]。如果 s[j]>= g[i],我們可以將這個餅乾 j 分配給孩子 i ,這個孩子會得到滿足。你的目標是儘可能滿足越多數量的孩子,並輸出這個最大數值。

public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        
int i = 0; int j = 0; while(i < g.length && j < s.length){ if(s[j] >= g[i]){ j++; i++; }else{ j++; } } return i; }

二、不重疊的區間的個數

435.無重疊區間 medium 2021-06-10

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

注意:

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

public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals, new Comparator<int[]>(){
            @Override
            public int compare(int[] o1, int[] o2){
                //return o1[1] - o2[1]; 儘量不用,防止溢位
                return
(o1[1] < o2[1]) ? -1 : ((o1[1] == o2[1]) ? 0 : 1); } }); int count = 1; int end = intervals[0][1]; for(int i = 1; i < intervals.length; i++){ if(intervals[i][0] < end){ continue; }else{ end = intervals[i][1]; count++; } } return intervals.length - count; }

三、投飛鏢刺破氣球

452. 用最少量的劍引爆氣球 medium2021-06-10

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

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

給你一個數組 points ,其中 points [i] = [xstart,xend] ,返回引爆所有氣球所必須射出的最小弓箭數。

此題與上題一樣,尋找不重疊區間的個數,不過是開區間與閉區間的區別。

此題的關鍵在於讓一個二維陣列根據第二維資料進行排序,需要重寫compare演算法。

public int findMinArrowShots(int[][] points) {
        Arrays.sort(points, new Comparator<int[]>(){
            @Override
            public int compare(int[] o1, int[] o2){
                //return o1[1] - o1[2];
                return (o1[1] < o2[1]) ? -1 : ((o1[1] == o2[1]) ? 0 : 1);  
            }
        });
        int count = 1;
        int end = points[0][1];
        for(int i = 1;  i < points.length; i++){
            if(points[i][0] <= end){
                continue;
            }else{
                count++;
                end = points[i][1];
            }
        }
        return count;
    }

四、根據身高和順序重組排隊

406. 根據身高重建佇列 medium 2021-06-11

假設有打亂順序的一群人站成一個佇列,陣列 people 表示佇列中一些人的屬性(不一定按順序)。每個 people[i] = [hi, ki] 表示第 i 個人的身高為 hi ,前面 正好 有 ki 個身高大於或等於 hi 的人。

請你重新構造並返回輸入陣列people 所表示的佇列。返回的佇列應該格式化為陣列 queue ,其中 queue[j] = [hj, kj] 是佇列中第 j 個人的屬性(queue[0] 是排在佇列前面的人)。

這個題需要先依次身高降序排列,然後將身高矮的向前移動,這樣不會改變身高高的人的k值。

public int[][] reconstructQueue(int[][] people) {
        //先按照hi進行排序降序,然後移動身高較矮的向前移,這樣不會影響K的值
        //按身高降序排序,同身高按k升序
        Arrays.sort(people, new Comparator<int[]>(){
            @Override
            public int compare(int[] o1, int[] o2){
                if(o1[0] - o2[0] > 0){ //降序
                    return -1; 
                }else if(o1[0] - o2[0] < 0){
                    return 1;
                }else{
                    if(o1[1] - o2[1] < 0){  //升序
                        return -1;  
                    }else if(o1[1] - o2[1] > 0){
                        return 1;
                    }else{
                        return 0;
                    }
                }
            }
        });
        for(int i = 0; i < people.length; i++){
            if(i <= people[i][1]) {
                continue;
            }else{
                int[] temp = people[i];
                int tempIndex = people[i][1];
                //for迴圈中需要使用tempIndex,不要使用people[i][1],因為它在變化
                for(int j = i; j > tempIndex; j--){
                    people[j] = people[j - 1];
                }
                people[tempIndex] = temp;
            }
        }
        return people;
    }

五、買賣股票的最大收益

121.買賣股票的最佳時機 easy 2021-06-11

給定一個數組 prices ,它的第i 個元素prices[i] 表示一支給定股票第 i 天的價格。

你只能選擇 某一天 買入這隻股票,並選擇在 未來的某一個不同的日子 賣出該股票。設計一個演算法來計算你所能獲取的最大利潤。

返回你可以從這筆交易中獲取的最大利潤。如果你不能獲取任何利潤,返回 0 。

最簡單的想法當然是巢狀遍歷,然後就超時了。

記錄之前最小值,然後計算今天賣出的利潤, 一次遍歷就夠。

public int maxProfit(int[] prices) {
        int maxProfit = 0;
        int minPrice = prices[0];
        for(int i = 1; i < prices.length; i++){
            if(prices[i] <= minPrice){
                minPrice = prices[i];
            }else{
                maxProfit = Math.max(maxProfit, prices[i] - minPrice);
            }
        }
        return maxProfit;
    }

六、買賣股票的最大收益Ⅱ

122.買賣股票的最佳時機Ⅱ easy 2021-06-11

給定一個數組 prices ,其中prices[i] 是一支給定股票第 i 天的價格。

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

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

這個題我最初的思想是利用數學中類似導數的思想,找出買入時機和賣出時機。將問題複雜化了很多。事實上,只需要遍歷然後累加股票在上升期的收益就行了,不需要精確地將買入賣出點定位。

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

七、種植花朵

605. 種花問題 easy2021-06-12

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

給你一個整數陣列flowerbed 表示花壇,由若干 0 和 1 組成,其中 0 表示沒種植花,1 表示種植了花。另有一個數n ,能否在不打破種植規則的情況下種入n朵花?能則返回 true ,不能則返回 false。

public boolean canPlaceFlowers(int[] flowerbed, int n) {
        int len = flowerbed.length;
        if(len == 0 || (len == 1 && flowerbed[0] == 1)){
            return n == 0;
        }
        if(len == 1 && flowerbed[0] == 0){
            return n <= 1;
        }
        int insertNum = 0;
        if(flowerbed[0] == 0 && flowerbed[1] == 0){
            insertNum ++;
            flowerbed[0] = 1; 
        }
        for(int i = 0; i < len - 3; i++){
            if(flowerbed[i] == 1 && flowerbed[i + 1] == 0 && flowerbed[i + 2] == 0 && flowerbed[i + 3] == 0){
                flowerbed[i + 2] = 1;
                insertNum++;
                i++;
            }
        }
        if(flowerbed[len - 2] == 0 && flowerbed[len - 1] == 0){
            flowerbed[len - 1]= 1;
            insertNum++;
        }
        if(insertNum >= n){
            return true;
        }else{
            return false;
        }
    }

八、判斷是否為子序列

392. 判斷子序列 easy2021-06-12

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

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

進階:

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

public boolean isSubsequence(String s, String t) {
        if(s.length() > t.length()) return false;
        char[] sArray = s.toCharArray();
        char[] tArray = t.toCharArray();
        int i = 0; int j = 0;
        while(i < sArray.length && j < tArray.length){
            if(tArray[j] == sArray[i]){
                i++;
                j++;
            }else{
                j++;
            }   
        }
        if(i == sArray.length){
            return true;
        }else{
            return false;
        }
    }

九、修改一個數組為非遞減陣列

665.非遞減陣列 medium2021-06-12

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

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

這道題關鍵在於找出區域性最值,需要特別注意的是,區域性最大值也伴隨著區域性最小值。是否將區域性最大進行修改還是將最小進行修改需要進行判斷。

public boolean checkPossibility(int[] nums) {
        int len = nums.length;
        if(len <= 2) return true;
        int oper = 1;
        if(nums[0] > nums[1]){
            nums[0] = nums[1];
            oper--;
        }
        if(nums[len - 1] < nums[len - 2]){
            nums[len - 1] = nums[len - 2];
            oper--;
        }
        for(int i = 1; i < len - 1; i++){
            if(nums[i] <= nums[i + 1]){
                continue;
            }
            oper--;
            if(nums[i + 1] >= nums[i - 1]){
                nums[i] = nums[i - 1];
            }else{
                nums[i + 1] = nums[i];
            }
        }
        return oper >= 0;
    }

十、子陣列最大的和

53.最大子序和 easy2021-06-12

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

public int maxSubArray(int[] nums) {
        int max = nums[0];
        for(int num : nums){
            if(max < num) max = num;
        }
        if(max <= 0){
            return max;
        }else{
            //下面的演算法是針對陣列中有正數的情況下的。
            int partSum = 0;
            int maxPartSum = 0;
            for(int i = 0; i < nums.length; i++){
                if(partSum + nums[i] <= 0){
                    partSum = 0;
                    continue;
                }
            partSum = partSum + nums[i];
            maxPartSum = Math.max(partSum, maxPartSum);
            }
            return maxPartSum;
        }      
    }

十一、分隔字串使同樣字元出現在一起

763. 劃分字母區間 medium2021-06-12

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

這個題的關鍵在於使用一個數組記錄每個字母出現的最後位置,然後比較當前小片段末尾位置與片段中出現的字母的最後位置進行對比。

public List<Integer> partitionLabels(String s) {
        int[] last = new int[26];
        for(int i = 0; i < s.length(); i++){
            last[s.charAt(i) - 'a'] = i;
        }
        List<Integer> list = new ArrayList<>();
        int start = 0; 
        int end = 0;
        for(int i = 0; i < s.length(); i++){
            end = Math.max(end, last[s.charAt(i) - 'a']);
            if(i == end){
                list.add(end - start + 1);
                start = end + 1;
            }
        }
        return list;

    }