LeetCode解題記錄(貪心演算法)(二)

進擊的汪sir發表於2021-07-18

1. 前言

由於後面還有很多題型要寫,貪心演算法目前可能就到此為止了,上一篇部落格的地址為
LeetCode解題記錄(貪心演算法)(一)
下面正式開始我們的刷題之旅

2. 貪心

763. 劃分字母區間(中等)

題目連結

思路

想切割,要有首尾兩個指標,確定了結尾指標,就能確定下一個切割的開始指標。
遍歷字串,如果已掃描部分的所有字元,都只出現在已掃描的範圍內,即可做切割。
注意 : 貪心的思想為,只要是掃描過的字元,都出現在我掃描的範圍之類,我就切割,不去考慮其他的條件,這樣能保證切割的數量最多

程式碼實現思路

  • result 用來儲存結果
  • start end,片斷的首尾指標
  • map 存放每一個字元的最遠位置

首先通過一個map,記錄了每個字元的最遠的位置,接下來遍歷字串,end是用來記錄,當前已經掃描的字串的最遠的出現的位置,如果當前的位置,等於掃描的字串的最遠位置,則,可以證明,到達了切割的條件,然後切割,存到result裡

class Solution {
public:
    vector<int> partitionLabels(string S) {
        vector<int> result;
        unordered_map<char, int> map; //記錄char c 和其最後出現位置的 map
        int start = 0, end = 0;
        // 初始化map,記錄每一個字元的最後位置
        for (int i = 0; i < S.size(); i ++) {
            map[S[i]] = i;
        }
        for (int i = 0; i < S.size(); i ++) {
            end = max(end, map[S[i]]);//記錄已掃描字元的最後一次出現的位置
            if (i == end) {//說明後面的片段沒有出現重複的字母了
                result.push_back(end - start + 1);//記錄結果
                start = i + 1;
            }
        }
        return result;
    }
};

406. 根據身高重建佇列(中等)

解題思路

貪心演算法:按照身高從高到低進行排序,矮的放後面,因為矮的即使放在了高的前面,也不會對之前高的產生影響;但高的放在前面,對矮的結果就會產生影響了。

重寫 compare() 方法:身高相同,按照個數升序排序;身高不同,按照身高降序排序。

public int compare(int[] o1, int[] o2) {

    // 先按身高降序,若身高相同則按 k 值升序。
    return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0];
}

第二個數字作為索引位置,把陣列放在目標索引位置上,如果原來有數了,會往後移。(在一個 ListList 中的指定位置插入一個元素,當前指定位置的元素會往後面移動一個位置。)
遍歷排序後的陣列,根據 K 插入到 K 的位置上。

class Solution {

    public int[][] reconstructQueue(int[][] people) {

        int n = people.length;
        int m = people[0].length;
        if (n == 0 || m == 0) return new int[0][0];

        Arrays.sort(people, new Comparator<int[]>() {

            @Override
            public int compare(int[] o1, int[] o2) {

                // 先按身高降序,若身高相同則按 k 值升序。
                return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0];
            }
        });

        // 遍歷排序後的陣列,根據 K 插入到 K 的位置上。
        List<int[]> list = new ArrayList<>();
        for (int[] i : people) {

            list.add(i[1], i);
        }
        return list.toArray(new int[list.size()][2]);
    }
}


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


題解:

貪心演算法

本題是要維持一個非遞減的數列,所以遇到遞減的情況時(nums[i] > nums[i + 1]),要麼將前面的元素縮小,要麼將後面的元素放大。

但是本題唯一的易錯點就在這,

如果將nums[i]縮小,可能會導致其無法融入前面已經遍歷過的非遞減子數列;
如果將nums[i + 1]放大,可能會導致其後續的繼續出現遞減;
所以要採取貪心的策略,在遍歷時,每次需要看連續的三個元素,也就是瞻前顧後,遵循以下兩個原則:

需要儘可能不放大nums[i + 1],這樣會讓後續非遞減更困難;
就是能不放大就不放大,儘量與前面持平

如果縮小nums[i],但不破壞前面的子序列的非遞減性;

演算法步驟:

遍歷陣列,如果遇到遞減:
還能修改:
修改方案1:將nums[i]縮小至nums[i + 1];
這個方案是,用i-1,i,i+1,來表示三個數的位置,其中i是我們發現大於i+1的數,那麼當i+1大於i-1的時候,我們應該將i縮小至i+1,為什麼呢,你想啊,你右邊的數比你小,你左邊的數比你小,但是呢,你右邊的數比你左邊的數高,你是不是隻需要和你右邊的一樣高,就能保持非遞減?如果你不這樣,你讓右邊的數增加,這就違反了上面的第一條原則:需要儘可能不放大nums[i + 1],這樣會讓後續非遞減更困難,因為這種情況,我們的選擇是縮小i

修改方案2:將nums[i + 1]放大至nums[i];
這種情況是什麼呢,與上一種相反,你左邊右邊的數都比你小,但是你右邊的數比你左邊的數要小,這個時候我們就應該將右邊的數放大,放大至nums[i],也就是你的大小,這種情況下是沒有爭論的,只能將右邊的數放大,不明白的同學可以自己在紙上畫一畫

如果不能修改了:直接返回false;
這個程式碼需要修改兩個地方,顯然不符合題目要求,返回false

程式碼如下

flag 代表修改機會,因為只有一次,所以用掉了,flag就變成false

class Solution {
public:
    bool checkPossibility(vector<int>& nums) 
    {
        if (nums.size() == 1)   return true;
        bool flag = nums[0] <= nums[1] ? true : false; // 標識是否還能修改
        // 遍歷時,每次需要看連續的三個元素
        for (int i = 1; i < nums.size() - 1; i++)
        {
            if (nums[i] > nums[i + 1])  // 出現遞減
            {
                if (flag)   // 如果還有修改機會,進行修改
                {
                    if (nums[i + 1] >= nums[ i - 1])// 修改方案1
                        nums[i] = nums[i + 1];
                    else                            // 修改方案2
                        nums[i + 1] = nums[i];      
                    flag = false;                   // 用掉唯一的修改機會
                }   
                else        // 沒有修改機會,直接結束
                    return false;
            }
        }
        return true;
    }
};

執行結果
在這裡插入圖片描述

3. 總結

對不起各位,我演算法這塊更新的實在是太慢了,不過最近真的很忙很忙,當然有時候我也會娛樂一下,沒有做到自律,下個星期我還準備開始寫計算機網路和作業系統的專欄,所以演算法這塊,我儘量多寫(其實我就是懶,有時候晚上回家真的好累,不想寫演算法題,,,,)

總之,下個星期,繼續努力,與昨天的自己比較!

相關文章