【算法训练营Day24】贪心算法part2

买卖股票的最佳时机 II

题目链接:122. 买卖股票的最佳时机 II

解题逻辑:

本题的局部最优在于:当前持有的股票如果明天继续持有赚的没有今天多或者还亏了,那么今天就抛售

class Solution {
    public int maxProfit(int[] prices) {
        boolean buy = true;
        int sum = 0;
        int maxSum = Integer.MIN_VALUE;
        for(int i = 0;i < prices.length;i++) {
            if(buy) {
                sum -= prices[i];
                buy = false;
            }
            if(i + 1 < prices.length && prices[i + 1]  > prices[i]) continue;
            sum += prices[i];
            if(sum > maxSum) maxSum = sum;
            buy = true;
        }
        
        return maxSum;
    }
}

跳跃游戏

题目链接:55. 跳跃游戏

解题逻辑:

使用队列,这个队列的作用就是维护一个能够到达的区间(有点层序遍历二叉树的味道)

这个方法的思想是先将数组的第一个元素(也就是起点)能够到达的元素入队,接下来如果队头能够到达的元素在队列中,那么就将这个元素弹出去。而如果队头所能到达的元素不在队列中,则将数组中对应的元素入队。如此若最后数组中还有元素没有入队则返回false,其余情况返回true。

代码如下:

class Solution {
    public boolean canJump(int[] nums) {
        int pointer = 0;
        Deque<Integer> queue = new ArrayDeque<>();
        if(nums[pointer] + 1 >= nums.length) return true;
        int addNum = nums[pointer] + 1;
        for(int i = 0;i < addNum;i++) queue.add(nums[pointer++]);
        while(!queue.isEmpty()) {
            if(queue.element() + 1 <= queue.size()) queue.remove();
            else {
                int need = queue.element() + 1 - queue.size();
                if(need > nums.length - pointer) return true;
                for(int i = 0;i < need;i++) queue.add(nums[pointer++]);
            }
        } 
        if(pointer >= nums.length) return true;
        return false;
    }
}

跳跃游戏 II

题目链接:45. 跳跃游戏 II

本题的贪心贪的是什么?这里很容易陷入一个误区那就是贪得是跳跃的长度,也就是每次都选跳跃长度最长的,这样最后跳跃的次数肯定也少。

但是此题真正应该贪得是跳跃的距离,每次取最长的距离,如此最后才会保证跳跃的次数最少。

从代码上来说跳跃的长度是nums[i],而跳跃的距离就是nums[i] + i

解题逻辑:

  • 初始化双指针left、right代表当前所能到达的范围
  • 在该区间中找到跳跃距离最远的点作为下一跳
  • 计数
  • 如此循环直到right >= nums.length - 1为止
class Solution {
    public int jump(int[] nums) {
        if(nums.length == 1) return 0;
        int count = 0;
        int nextJump = 0;
        while(true) {
            int left = nextJump;
            int right = nums[left] + left;
            if(right >= nums.length - 1) return count + 1;
            int maxIndex = 0;
            for(int i = left;i <= right;i++) {
                int currentIndex = nums[i] + i;
                if(currentIndex>= maxIndex) {
                    maxIndex = currentIndex;
                    nextJump = i;
                }
            }
            count++;
        }
    }
}   

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

题目链接:1005. K 次取反后最大化的数组和

解题逻辑:

这道题的局部最优很好想就是将这个取反给最小的负数即可,没有负数或者有多余的取反操作则给最小的正数即可。

解题代码:

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        Arrays.sort(nums);
        int negative = 0;
        for(int num : nums) if(num < 0) negative++;
        if(k <= negative) for(int i = 0;i < k;i++) nums[i] = -nums[i];
        else {
            int result = (k - negative) % 2;
            if(result == 0) for(int i = 0;i < negative;i++) nums[i] = -nums[i];
            else {
                if(negative == 0)  nums[0] = -nums[0];
                else {
                    for(int i = 0;i < negative;i++) nums[i] = -nums[i];
                    int num1 = nums[negative - 1];
                    int num2 = Integer.MAX_VALUE;
                    if(negative < nums.length) num2 = nums[negative];
                    int index = num1 > num2 ? negative : negative - 1;
                    nums[index] = -nums[index];
                }

            }
        }

        int sum = 0;
        for(int num : nums) sum += num;
        return sum;
        
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十八岁讨厌编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值