LeetCode刷题day17——贪心

周日休息,昨天有个课程作业说是晚上开会,等了很久一直没开,也没跑步,卡在摆动序列,题也没打完…今天补上!今天晚上跑!热血沸腾,活力满满的一天!

376. 摆动序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 **摆动序列 。**第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

  • 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
  • 相反,[1, 4, 7, 2, 5][1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列最长子序列的长度

示例 1:

输入:nums = [1,7,4,9,2,5]
输出:6
解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。

示例 2:

输入:nums = [1,17,5,10,13,15,10,5,16,8]
输出:7
解释:这个序列包含几个长度为 7 摆动序列。
其中一个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8) 。

示例 3:

输入:nums = [1,2,3,4,5,6,7,8,9]
输出:2

提示:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 1000

**进阶:**你能否用 O(n) 时间复杂度完成此题?

//思路写在注释,这题没做过,还真蛮难想的,题解里有一个是动态规划,但是暂时让我逃避一下吧,等到了动态规划再说,继续贪心先,局部最优推出全局最优
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        //此题的思路很巧妙,不是删除元素,而是转化为去找极值问题
        if (nums.size() <= 1)//先选出特殊的
            return nums.size();
        int cur = 0;
        int pre = 0;//假设添加了一个元素在头部,形成开头平坡
        int res = 1;//默认尾部直接算一个峰值,现在从头开始找峰值
        for (int i = 0; i < nums.size() - 1; i++) {
            cur = nums[i + 1] - nums[i];
            if ((pre >= 0 && cur < 0) || (pre <= 0 && cur > 0)) {//只允许左边平坡
                res++;
                pre = cur;//可以理解为,把cur加入我们的结果数组
            }
        }
        return res;
    }
};

738. 单调递增的数字

当且仅当每个相邻位数上的数字 xy 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增

示例 1:

输入: n = 10
输出: 9

示例 2:

输入: n = 1234
输出: 1234

示例 3:

输入: n = 332
输出: 299

提示:

  • 0 <= n <= 109

分析:

暴力法:from n to 0,从n不断减小遍历,每次都检查是否符合单调递增的条件,优点是:方法简单;缺点是:当n太大时,会超出时间限制。

class Solution {
public:
    int monotoneIncreasingDigits(int n) {

        for (int i = n; i >=0; i--) {
            int k=i;
            vector<int> ans;
            int sum=0;
            int flag=1;
            while(i/10>0) {
                ans.push_back(i%10);
                i/=10;
            }
            ans.push_back(i%10);
            /*out<<"***"<<endl;
            for(int j=0;j<ans.size();j++)
                cout<<ans[j]<<" ";
            cout<<endl;*/
            i=k;
            reverse(ans.begin(), ans.end());
            for(int j=0;j<ans.size()-1;j++) {
                if(ans[j]<=ans[j+1]) {
                    sum=sum*10+ans[j];
                    continue;
                }
                else {
                    flag=0;
                    break;
                }
            }
            sum=sum*10+ans[ans.size()-1];
            if(flag==1) {
                cout<<sum<<endl;
                return sum;
            }
        }
        return 0;
    }
};

贪心算法可以高效地解决这个问题,避免逐个数字递减。具体思路是:

  1. 从左到右遍历数字:将数字转为字符串逐位检查。
  2. 遇到下降点:当发现某一位数字大于其后继位数字时(即下降点),说明从该位置开始需要调整。
  3. 调整数字:将下降点前一位的数字减一,并将后面的所有位数都改为 9,这样确保数字仍为最大单调递增数。
  4. 处理进位:如果减小某一位后,可能会导致更早的位也需要调整,因此从右向左继续修正,直到没有下降为止。
class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string s = to_string(n);
        for (int i = 0; i < s.length()-1; ) {
            if(s[i]>s[i+1]) {
                s[i]-=1;
                for(int j = i+1; j < s.length(); j++) {
                    s[j]='9';
                }
                i=0;
            }
            else
                i++;
        }
        return stoi(s);

    }
};

122. 买卖股票的最佳时机Ⅱ

给你一个整数数组 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 * 104
  • 0 <= prices[i] <= 104

分析:

算法的核心思想是通过计算相邻价格之间的差值,并累加所有正差值来获得最大利润。为什么可以这样做?

拿到这道题的第一想法是:利润就是某个大区间的差,比如:示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
最大总利润为 4 。

利润是day5-day1。这样想就复杂了,是的!或许可以想成:(day5-day4)+(day4-day3)+(day3-day2)+(day2-day1) = day5-day1。这样只需要求每天的差值,找利润是正数时,买入卖出。贪心策略:只收集正利润。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<int> bonus;
        int sum = 0;
        for (int i = 0; i < prices.size() - 1; i++) {
            bonus.push_back(prices[i + 1] - prices[i]);
            if (bonus.back() > 0)
                sum += bonus.back();
        }
        return sum;
    }
};

121. 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 104

分析:

该算法基于贪心策略,目标是通过每次选择当前最优的买入和卖出时机来获得最大利润。核心思想是:

  • 遍历股票价格,维护当前的最低买入价格 minCost 和最大利润 maxP
  • 对于每一天的价格,计算当前卖出的利润并更新最大利润 maxP
  • 最终返回最大利润,若无利润则返回 0。

通过一次遍历完成,时间复杂度为 O(n)。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int maxP = 0;
        int minCost = INT_MAX;
        for (int i = 0; i < prices.size(); i++) {
            minCost = min(minCost, prices[i]);
            maxP = max(maxP, prices[i] - minCost);
        }
        return maxP >= 0 ? maxP : 0;
    }
};

另外,暴力法能通过大部分样例,但是某些样例时间超限:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int max=INT_MIN;
        for (int i = 0; i < prices.size()-1; i++) {
            if(prices[i+1]<=prices[i])//后一天价格更高,前一天就不适合买入了
                continue;
            for (int j = i + 1; j < prices.size(); j++) {
                int profit = prices[j] - prices[i];
                if(profit > max) max = profit;
            }
        }
        return max>=0?max:0;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值