程式碼隨想錄演算法訓練營day32 | leetcode 122. 買賣股票的最佳時機 II、55. 跳躍遊戲、45. 跳躍遊戲 II

Humphreyr發表於2024-03-25

目錄
  • 題目連結:122. 買賣股票的最佳時機 II-中等
  • 題目連結:55. 跳躍遊戲-中等
  • 題目連結:45. 跳躍遊戲 II-中等

題目連結:122. 買賣股票的最佳時機 II-中等

題目描述:

給你一個整數陣列 prices ,其中 prices[i] 表示某支股票第 i 天的價格。

在每一天,你可以決定是否購買和/或出售股票。你在任何時候 最多 只能持有 一股 股票。你也可以先購買,然後在 同一天 出售。

返回 你能獲得的 最大 利潤

示例 1:

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

示例 2:

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

示例 3:

輸入:prices = [7,6,4,3,1]
輸出:0
解釋:在這種情況下, 交易無法獲得正利潤,所以不參與交易可以獲得最大利潤,最大利潤為 0 。

提示:

  • 1 <= prices.length <= 3 * 10^4
  • 0 <= prices[i] <= 10^4

如果每天都有利潤,那麼某一天買進,到某一天賣出,其實等於在這個區間每天買進賣出

其實我們需要收集每天的正利潤就可以,收集正利潤的區間,就是股票買賣的區間,而我們只需要關注最終利潤,不需要記錄區間

那麼只收集正利潤就是貪心所貪的地方!

區域性最優:收集每天的正利潤,全域性最優:求得最大利潤

程式碼如下:

// 時間複雜度: O(n)
// 空間複雜度: O(1)
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int res = 0;
        for (int i = 1; i < prices.size(); ++i) {
            if (prices[i] - prices[i - 1] > 0)
                res += (prices[i] - prices[i - 1]);
        }
        return res;
    }
};

題目連結:55. 跳躍遊戲-中等

題目描述:

給你一個非負整數陣列 nums ,你最初位於陣列的 第一個下標 。陣列中的每個元素代表你在該位置可以跳躍的最大長度。

判斷你是否能夠到達最後一個下標,如果可以,返回 true ;否則,返回 false

示例 1:

輸入:nums = [2,3,1,1,4]
輸出:true
解釋:可以先跳 1 步,從下標 0 到達下標 1, 然後再從下標 1 跳 3 步到達最後一個下標。

示例 2:

輸入:nums = [3,2,1,0,4]
輸出:false
解釋:無論怎樣,總會到達下標為 3 的位置。但該下標的最大跳躍長度是 0 , 所以永遠不可能到達最後一個下標。

提示:

  • 1 <= nums.length <= 10^4
  • 0 <= nums[i] <= 10^5

遍歷思路:

可以從後往前遍歷,如果當前位置能夠到達所記錄的最後一個位置,那對其進行迭代,只要確保每個位置都能到達,即最後落在第一個下標,那說明可以跳到終點,如果不能落在第一個下標,那說明中間有地方斷了,即不能到達。

程式碼如下:

// 時間複雜度: O(n)
// 空間複雜度: O(1)
class Solution {
public:
    bool canJump(vector<int>& nums) {
        int index = nums.size() - 1;
        for (int i = nums.size() - 2; i >= 0; --i) {
            if (nums[i] >= index - i)
                index = i;
        }
        if (index == 0)
            return true;
        return false;
    }
};

貪心思路:

這個問題轉化為跳躍覆蓋範圍究竟可不可以覆蓋到終點!

每次移動取最大跳躍步數(得到最大的覆蓋範圍),每移動一個單位,就更新最大覆蓋範圍。

貪心演算法區域性最優解:每次取最大跳躍步數(取最大覆蓋範圍),整體最優解:最後得到整體最大覆蓋範圍,看是否能到終點

程式碼如下:

// 時間複雜度: O(n)
// 空間複雜度: O(1)
class Solution {
public:
    bool canJump(vector<int>& nums) {
        if (nums.size() == 1)
            return true;
        int cover = 0;
        for (int i = 0; i <= cover; ++i) {
            cover = max(cover, i + nums[i]);
            if (cover >= nums.size() - 1)
                return true;
        }
        return false;
    }
};

題目連結:45. 跳躍遊戲 II-中等

題目描述:

給定一個長度為 n0 索引整數陣列 nums。初始位置為 nums[0]

每個元素 nums[i] 表示從索引 i 向前跳轉的最大長度。換句話說,如果你在 nums[i] 處,你可以跳轉到任意 nums[i + j] 處:

  • 0 <= j <= nums[i]
  • i + j < n

返回到達 nums[n - 1] 的最小跳躍次數。生成的測試用例可以到達 nums[n - 1]

示例 1:

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

示例 2:

輸入: nums = [2,3,0,1,4]
輸出: 2

提示:

  • 1 <= nums.length <= 10^4
  • 0 <= nums[i] <= 1000
  • 題目保證可以到達 nums[n-1]

如果移動下標達到了當前這一步的最大覆蓋最遠距離了,還沒有到終點的話,那麼就必須再走一步來增加覆蓋範圍,直到覆蓋範圍覆蓋了終點

45.跳躍遊戲II

程式碼如下:

// 時間複雜度: O(n)
// 空間複雜度: O(1)
class Solution {
public:
    int jump(vector<int>& nums) {
        if (nums.size() == 1)
            return 0;
        int res = 0;
        int cover = 0; // 當前覆蓋最遠距離下標
        int next = 0; // 下一步覆蓋最遠距離下標
        for (int i = 0; i < nums.size() - 1; ++i) {
            next = max(next, i + nums[i]); // 更新下一步覆蓋最遠距離下標
            if (cover == i) { // 遇到當前覆蓋最遠距離下標
                ++res;			// 需要走下一步
                cover = next;	// 更新當前覆蓋最遠距離下標
                // 下面這一步判斷可免,不過會繼續遍歷到終點
                if (cover >= nums.size() - 1) // 當前覆蓋最遠距到達集合終點,不用做ans++操作了,直接結束
                    break;
            }
        }
        return res;
    }
};

相關文章