【力扣刷题】(四)贪心专题

贪心

1,贪心的算法思想

贪心本质是求解局部最优,从而全局最优。

assert:动规的原因是两个限制条件是互相干扰的,而贪心的子条件相互独立,所以才能确保局部最优到全局最优。因为有很多情况,局部最优不一定是全局最优,这个前提就是全局可以等价于局部的累加。

2,力扣题型

376,摆动序列

https://leetcode-cn.com/problems/wiggle-subsequence/

摆动序列就是一个上下波动的序列,如果不符合就要采用子序列的定义删掉这个波值,只不过这里的删掉不是真正的删掉数组元素,而是跳过不将其统计即可。所以我们的值是可以继续累加的,因为不符合的元素可以删掉。

(下面据说是一个优化的dp)

class Solution {
    public int wiggleMaxLength(int[] nums) {
        if(nums.length < 2) return nums.length;
        if(nums.length == 2 && nums[0]!=nums[1]) return 2;
        // 上坡和下坡计数
        int up=1,down = 1;
        for(int i=1;i<nums.length;i++){
            // down没变就不增,down变了其实在down上增
            if(nums[i]>nums[i-1]) up = down +1;
            // up没变就不增
            if(nums[i]<nums[i-1]) down = up + 1;
        }
        return Math.max(up,down);
    }
}

贪心思路:

class Solution {
    public int wiggleMaxLength(int[] nums) {
        // 贪心思路:局部最优累加即全局最优
        if(nums.length < 2) return nums.length;
        // 分别定义当前差值和前一个差值区分是上坡还是下坡
        int prediff = 0;
        int currdiff = 0;
        int count = 1;
        for(int i=1;i<nums.length;i++){
            currdiff = nums[i] - nums[i-1];
            // 边界问题,等于0表示为了满足初始时的情况
            if((currdiff < 0 && prediff >=0) || (currdiff >0 && prediff <=0)){
                count++;
                prediff = currdiff;
            }
        }
        return count;
    }
}

53,最大子序和

https://leetcode-cn.com/problems/maximum-subarray/

class Solution {
    public int maxSubArray(int[] nums) {
        // sum初始最小,目的一定要取到值
        // sum,count分别记录全局和局部最优
        int sum = Integer.MIN_VALUE;
        int count = 0;
        for(int i=0;i<nums.length;i++){
           count += nums[i];
           sum = Math.max(count,sum);
        //    当count小于0时,立马置位0,表示下次直接跳过之前的累积和
        // 放在最后判断,也是为了sum能够记录到之前的局部最优
           if(count <= 0) count = 0;
        }
        return sum;
    }
}

122,买卖股票的最佳时机II

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

两天为一个交易日。不要想的复杂,简化:第一天买入和第三天卖出没有绝对关系,因为prices[1]-prices[2]+prices[2]-prices[3];所以其实等价于前后两天的一个收益,也就是局部收益最高全局就最高。

class Solution {
    public int maxProfit(int[] prices) {
        // 多次买卖一支股票:即当天卖出的当天可以买入
        // 所以不需要考虑当天卖出的是否最大,因为当天还可以买
        int in = 0, out = 0,result = 0;
        for(int i=1;i<prices.length;i++){
            if(prices[i-1]<prices[i]){
                in = prices[i-1];
                out = prices[i];
                result += (out -in);
            }
        }
        return result;
    }
}

714,买卖股票的最佳时机含手续费

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/

class Solution {
    public int maxProfit(int[] prices, int fee) {
        // 因为有了手续费,不能简单的买了再卖
        // 需要记录最小值
        int min = prices[0];
        int res = 0;
        for(int i=1;i<prices.length;i++){
            if(prices[i]<min){
                min = prices[i];
            }
            // 卖出,可能会多次计算利润
            else if(prices[i]-fee>min){
                res += prices[i]-fee-min;
                // 但是每次计算利润都减去了手续费,确保卖出只减一次
                min = prices[i]-fee;
            }
            //还有第三种情况:不买不卖,可省略
            else if(prices[i]>=min && prices[i]-fee<=min){
                continue;
            }
        }
        return res;
    }
}

55,跳跃游戏

https://leetcode-cn.com/problems/jump-game/

class Solution {
    public boolean canJump(int[] nums) {
        // 贪心算法
        // 每个位置能够到达的最远距离,更新位置的最大值,当最终到达的距离大于最长距离(length-1)时则能到达
        int s = nums.length;
        int maxDis = nums[0];
        if(s<=1) return true;
        // 尝试从当前位置一步一步走,最多走s-1步!
        for(int i=1;i<s-1;i++){
            // 走的步数要小于当前最远距离,则更新最远距离为当前位置值(下次能够走的)+已走的距离
            if(i<=maxDis){
                maxDis = Math.max(maxDis,nums[i]+i);
            }else{
                break;
            }
        }
        return maxDis >= s-1;
    }
}
class Solution {
    public boolean canJump(int[] nums) {
        int s = nums.length;
        if (s == 1) {
            return true;
        }
        //覆盖范围
        int coverRange = nums[0];
        //在覆盖范围内更新最大的覆盖范围,i表示走的步数
        for (int i = 0; i <= coverRange; i++) {
            coverRange = Math.max(coverRange, i + nums[i]);
            if (coverRange >= s - 1) {
                return true;
            }
        }
        return false;
    }
}

45,跳跃游戏 Ⅱ

https://leetcode-cn.com/problems/jump-game-ii/

class Solution {
    public int jump(int[] nums) {
        int s = nums.length;
        int count = 0;
        int curDistance = 0;
        int nextDistance = 0;
        
        for(int i=0;i<s-1;i++){
            nextDistance = Math.max(nextDistance,nums[i]+i);
            // 当当前下标等于当前最远覆盖距离时,说明走完了还没到达,则步数加1
            // 同时将最远距离覆盖当前距离
           if(i==curDistance){
               curDistance = nextDistance;
               count++;
           }
        }
        return count;
    }
}

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

https://leetcode-cn.com/problems/maximize-sum-of-array-after-k-negations/

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        
        int s = nums.length;
        int sum = 0;
        // 记录最小值
        int min = Integer.MAX_VALUE;
        Arrays.sort(nums);
        // 先反转再求和
        for(int i=0;i<s;i++){
           if(nums[i]<0 && k>0){
               nums[i] = -nums[i];
               k--;
           }
           sum += nums[i];
           min = Math.min(nums[i],min);
        }
        // 判断k是否消耗完
        // 是则直接返回,否则减掉剩余的k%2个的2倍最小值,还要把之前的减掉
        return k<=0 ? sum:(sum-(k%2)*2*min);
       
    }
    
}

134,加油站

https://leetcode-cn.com/problems/gas-station/

暴力,过程模拟:(但是JAVA提交超时)

for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历,要善于使用while

顺序遍历中取模运算并没有什么区别,环形遍历中可以实现循环顺序遍历的功能。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        // 暴力过程模拟
        int s = gas.length;
        for(int i=0;i<s;i++){
            int res = (gas[i]-cost[i]);
            int index = (i+1)%s;
            //到头了开始走环了,回到起点,此时index!=i;
            while(res>0 && index != i){
                res += (gas[i]-cost[i]);
                index = (i+1)%s;
            }
            // 如果走了一圈且还有油,返回
            if(index == i && res>=0) return i;
        }
        return -1;
    }
}
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        // 题意:返回能够环路一周的出发地
        // 为此:要省下最多的汽油,才能走得远
        // 折返的过程该怎么体现?需要模拟吗,不需要!一趟遍历的过程即可记录能否走了
        int s = gas.length;
        int run = 0,res = 0,start = 0;
        // 找到一个出发点
        for(int i = 0;i<s;i++){
            run += (gas[i]-cost[i]);
            res += (gas[i]-cost[i]);
            if(run < 0){
                start = i+1;
                run = 0;
            }
        }
        return res>=0?start:-1;
    }
}

135,分发糖果

https://leetcode-cn.com/problems/candy/submissions/

提示:当有两个维度时,一定要先确定一个维度;再确定另一个维度。

class Solution {
    public int candy(int[] ratings) {
        int s = ratings.length;
        // 为什么一个用数组一个用变量?方便后面遍历时通过数组下标访问对应的值比较
        int result = 0,right = 0;
        int[] left = new int[s];
        // 从前往后遍历识别左规则
        for(int i=0;i<s;i++){
            if(i>0 && ratings[i]>ratings[i-1]){
                left[i] = left[i-1]+1;
            }else{
                left[i] = 1; //初始化相当于
            }
        }
        // 从后往前遍历识别右规则
        for(int i=s-1;i>=0;i--){
            if(i<s-1 && ratings[i]>ratings[i+1]){
                right++;
            }else{
                right = 1; //初始化相当于
            }
            // 取最大的确保左右规则能满足条件
            result += Math.max(left[i],right);
        }
        return result;

    }
}

406,根据身高重建队列

https://leetcode-cn.com/problems/queue-reconstruction-by-height/

class Solution {
  public int[][] reconstructQueue(int[][] people) {
//        按照身高h从大到小排序,h相同时按照人数k从小到大排序
       Arrays.sort(people, new Comparator<int[]>() {
           @Override
           public int compare(int[] o1, int[] o2) {
//               升序是前一个减后一个,降序是后一个减前一个
               return o1[0]==o2[0] ? o1[1]-o2[1]:o2[0]-o1[0];
           }
       });

        LinkedList<int[]> queue = new LinkedList<>();
        for (int[] person : people) {
//            API:按照指定index插入element;在位置插入且不会覆盖!
//            person的位置由k决定,此时会不断地覆盖,身高最小的会覆盖到最前面
            queue.add(person[1], person);
        }
//        queue.toArray传入参数为数组维度即可
        return queue.toArray(new int[people.length][]);
    }
}

435,重叠子区间问题

https://leetcode-cn.com/problems/non-overlapping-intervals/submissions/

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        // 从左到右排序
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
        int count = 0;
        // 起始待比较位置
        int end = intervals[0][1];
        for(int i=1;i<intervals.length;i++){
            //前一个的尾部和后一个的头部比较,重叠,取最小的尾部以便后面有更多的区间容错
            if(end>intervals[i][0]){
                count++;
                end = Math.min(intervals[i][1],end);
            }else{
                end = intervals[i][1];
            }
        }
        return count;
    }
}

类似题目:用最少的数量引爆气球https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/comments/

736,划分区间

https://leetcode-cn.com/problems/partition-labels/solution/

class Solution {
    public List<Integer> partitionLabels(String s) {
        // 过程模拟
        ArrayList res = new ArrayList();
        char[] chars =  s.toCharArray();
        // 标记每个字符(字符用ASCII码区分)出现的最远位置
        int[] words = new int[26];
        for(int i=0;i<chars.length;i++){
            words[chars[i]-'a'] = i;
        }
        int idx = 0;
        int last = -1;
        for(int i=0;i<chars.length;i++){
            // 依据字母表,更新当前字符的最远位置,注意这里其实最后是取最远位置的字符
            idx = Math.max(idx,words[chars[i]-'a']);
            // 如果遍历到了最远位置
            if(idx==i){
                res.add(i-last);
                last = i;
            }
        }
        return res;
    }
}

56,合并区间

https://leetcode-cn.com/problems/merge-intervals/submissions/

class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
        ArrayList<int[]> res = new ArrayList();
        int start = intervals[0][0];
        int end = intervals[0][1];
        for(int i=1;i<intervals.length;i++){
            // 后一个和前一个相比,没重叠,直接添加并更新
            if(intervals[i][0]>end){
                res.add(new int[]{start,end});
                start = intervals[i][0];
                end = intervals[i][1];
            }else{
                // 有重叠更新最大的尾部
                end = Math.max(end,intervals[i][1]);
            }
        }
        // 会漏掉最后一个元素这里手动添加上
        res.add(new int[]{start,end});
        return res.toArray(new int[res.size()][]);
    }
}

3,总结

题目链接难度
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于力扣刷题C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷题中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷题中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷题中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷题中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷题中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷题中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷题中,使用 string 可以方便地处理字符串相关的问题。 9. 注意边界条件:在力扣刷题中,边界条件往往是解决问题的关键。需要仔细分析题目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷题中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值