代码随想录第28天|贪心算法part2

122买卖股票的最佳时机2

在这里插入图片描述
贪心算法最好能写出表达式,这样才好推导
假设在i天买入,j天卖出,则利润:price[j]-price[i] = (price[j]-price[j-1])+(price[j-1]+price[j-2])+...+(price[i+1]-price[i])
于是我们可以计算出相邻天数的价格差,如果为正,就加入结果,否则跳过在这里插入图片描述
如该图,4,5,3是正的,这代表:在4的前一天,价格为1买入,在5的那天,价格为10卖出,在3的前一天,价格为3的时候买入,价格为6的时候卖出,总利润为12

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.跳跃游戏

在这里插入图片描述
贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围),整体最优解:最后得到整体最大覆盖范围,看是否能到终点。
遍历每个位置,更新能到达的最大边界,如果当前遍历到的位置是之前所能跳跃到的最大位置且该位置所能跳跃的长度为0,且当前位置不为右边界的话,说明只能呆在该位置到不了右边界,返回false即可

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int r = 0;
        for (int i = 0; i < nums.size(); i++) {
            r = max(r, i + nums[i]);
            if (r == i && nums[i] == 0 && r != nums.size() - 1)
                return false;
        }
        if (r >= nums.size() - 1)
            return true;
        return false;
    }
};

45.跳跃游戏II

在这里插入图片描述
局部最优:当前可移动距离尽可能多走,如果还没到终点,步数再加一。整体最优:一步尽可能多走,从而达到最少步数。
需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖。

如果移动下标达到了当前这一步的最大覆盖最远距离了,还没有到终点的话,那么就必须再走一步来增加覆盖范围,直到覆盖范围覆盖了终点。因为到达了当前这一步的最大覆盖范围的时候,下一步的最大范围又更新过了,所以说必定走过更新覆盖范围的那一步
比如[2,3,1,1,4],第一步就更新了最大覆盖范围到下标为2,当前覆盖范围为2,第二步的时候更新了最大覆盖范围到下标4,而这个时候并没有走到下标2,所以当走到下标2的时候,就需要走一步且更新当前覆盖范围为4,因为是边界,所以退出循环。这意味着走到了第一步的覆盖范围的时候,因为没到最后右边界,所以需要跳到之前能够更新最大覆盖范围的那一步(都更新过了最大覆盖范围,说明肯定比当前覆盖范围小,比如3就在1的前面),就如从第一步跳到第二步,然后更新当前覆盖范围,这个时候说明能从第二步跳到最后了,所以结束循环。(第一步一定要起跳,所以更新覆盖范围的时候res要+1)
2->3->末尾4
在这里插入图片描述

class Solution {
public:
    int jump(vector<int>& nums) {
        if(nums.size() == 1) return 0;
        int next = 0;
        int cur = 0;
        int res = 0;
        for(int i = 0;i < nums.size(); i++){
            next = max(next,i+nums[i]);
            if(i == cur){
                res++;
                cur = next;
                if(cur >= nums.size() - 1) break;
            }
        }
        return res;
    }
};

1005.K次取反后最大化的数组和

在这里插入图片描述
自己的想法:先给数组排个序,然后从
只有负数
有负有正
只有正
三种情况分别分析

class Solution {
public:
    int getSum(vector<int>& nums) {
        int sum = 0;
        for (auto p : nums) {
            sum += p;
        }
        return sum;
    }
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        int sum = 0;
        int left = 0;
        // 1.没有负数
        if (nums[0] >= 0) {
            if (k % 2 != 0)
                nums[0] = -nums[0];
            sum = getSum(nums);
        }
        // 2.有负数
        else {
            for (int i = 0; i < nums.size(); i++) {
                if (nums[i] >= 0) {
                    left = i;
                    break;
                }
            }
            if (left == 0)
                left = nums.size();
            // 先把负数都变正
            for (int i = 0; i < left && k > 0; i++){
                nums[i] = -nums[i];
                k--;
            }
            if (k > 0 && k % 2 != 0) {
                if (left != nums.size()) {
                    if (nums[left - 1] < nums[left])
                        nums[left - 1] = -nums[left - 1];
                    else{
                        nums[left] = -nums[left];
                    }
                }
                else{
                    nums[left-1] = -nums[left-1];
                }
            }
            sum = getSum(nums);
        }
        return sum;
    }
};

可以把数组按照绝对值从大到小排序,从前遍历到后,碰见负数就将其变为正并且k–
如果负数都变成正数且k还有剩余的话
当剩余的k为偶数的时候就不用管
为奇数则表示一定有一个数要被变成负数,则让数组的最后一个取负即可

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end(),
             [](int a, int b) { return abs(a) > abs(b); });
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] < 0 && k > 0) {
                nums[i] = -nums[i];
                k--;
            }
        }
        // 如果k有剩余的情况下,那么负数一定都被纠正成正数了
        // 这个时候选绝对值最小的元素进行翻转即可
        if (k % 2)
            nums[nums.size() - 1] = -nums[nums.size() - 1];
        int res = 0;
        for (int a : nums)
            res += a;
        return res;
    }
};
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值