【Leetcode】贪心问题合集 | 摆动序列、K次取反最大和、加油站、分发糖果、柠檬水找零、根据身高重建队列、单调递增的数字

news2024/11/22 19:11:41

贪心问题感觉还是挺不好想的,因为每一题有每一题的策略,感觉只能尽量做过的记住了。

376 摆动序列

注意:是序列,而不是数组。

求最大摆动序列的长度,即求谷 / 峰的个数。

若走势不为一条直线。

起始count = 2,当trend的符号发生改变时,就增加count并修改trend,直到遍历完整个序列。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VuZBcDT2-1686374064373)(【Leetcode】贪心问题合集/image-20230609132443535.png)]

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        if (n < 2) {
            return n;
        }

        // 找到第一个不为0的trend
        int i = 1;
        int trend = 0;
        while (trend == 0 && i < n) {
            trend = nums[i] - nums[i - 1];
            i++;
        }

        // 如果遍历完序列,trend还是0,说明走势是一条直线
        if (trend == 0) {
            return 1;
        }

        // 起始的走势已知,count为2,遍历剩下的数字
        int count = 2;
        while (i < n) {
            if (trend * (nums[i] - nums[i - 1]) < 0) {
                // 小于0说明一定是遇到了谷或者峰,可以改变trend的方向,否则为平滑或者走势相同,不必处理
                count++;
                trend = nums[i] - nums[i - 1];
            }
            i++;
        }

        return count;
    }
}

53 最大子数组和

就算玛卡巴卡来了,这题也得是动态规划。

class Solution {
    public int maxSubArray(int[] nums) {
        // D[i] = Math.Max(nums[i], nums[i] + D[i - 1])
        int max = nums[0];
        int sum = nums[0];
        for (int i = 1; i < nums.length; i++) {
            sum = sum < 0 ? nums[i] : nums[i] + sum;
            max = Math.max(sum, max);
        }
        return max;
    }
}

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

贪心策略:概括地说,将最小的负数反转。

  1. k ≥ c o u n t 负数 k\geq count_{负数} kcount负数
    1. 多余可变换次数 k − c o u n t k-count kcount为偶数,全部元素均可变为非负数,最大数组和 s u m = ∑ ∣ n u m ∣ sum=\sum |num| sum=num
    2. 多余可变换次数为奇数,将最小绝对值元素变为负数,最大数组和 s u m = ∑ ∣ n u m ∣ − 2 × a b s m i n sum=\sum |num|-2\times abs_{min} sum=num2×absmin
  2. k < c o u n t 负数 k\lt count_{负数} k<count负数,将最小的 k k k个负数变为正数,由于数组元素具有范围,可使用代表 [ − 100 , 100 ] [-100,100] [100,100]的数组freq = new int[201]统计元素频率,然后顺序访问 f r e q freq freq并计算最大和。
class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        int n = nums.length;

        // 有多少个负数
        int count = 0;
        // 所有数字都变为正数后的和
        int sumAllPositive = 0;
        // 最小绝对值
        int minAbs = Integer.MAX_VALUE;

        // 计算上述三个变量
        for (int i = 0; i < n; i++) {
            if (nums[i] < 0) {
                count++;
            }
            int tmp = nums[i] < 0 ? -nums[i] : nums[i];
            sumAllPositive += tmp;
            minAbs = Math.min(minAbs, tmp);
        }

        // 如果操作次数大于负数个数和
        if (k >= count) {
            // 1、多余次数为偶数,全部元素可以都是正数
            if ((k - count) % 2 == 0) {
                return sumAllPositive;
            }
            // 2、多余次数为奇数,把有最小绝对值的数字变负
            else {
                return sumAllPositive - 2 * minAbs;
            }
        }

        // 3、操作次数小于负数个数的情况------------------------------------------------
        // 因为数字有范围,开一个数组作为哈希表,从而避免排序,空间复杂度$O(C)$

        // 统计freq
        int[] freq = new int[201];
        for (int i = 0;  i < n; i++) {
            freq[nums[i] + 100]++;
        }

        // 计算sum
        int sum = 0;
        for (int i = 0; i < 201; i++) {
            if (freq[i] == 0) {
                continue;
            }
            
            if (k <= 0) {
                // 如果能反转的都反转了,直接加就好了
                sum += (i - 100) * freq[i];
            }
            else {
                // 反转
                if (k >= freq[i]) {
                    sum += (100 - i) * freq[i];
                    k -= freq[i];
                }
                else {
                    // 反转k个
                    sum += (100 - i) * k;
                    sum += (i - 100) * (freq[i] - k);
                    k = 0;
                }
            }
        }

        return sum;
    }
}

134 加油站★

感觉这题贪心策略并不好想。

如果总油量减去总消耗量为负,说明无法绕圈(因为从任意点出发绕一圈的总油量-总消耗量都相等),否则可以。

贪心策略:记每个站点的剩余油量为 g a s [ i ] − c o s t [ i ] gas[i]-cost[i] gas[i]cost[i]istart从0开始,累加每个站点的剩余油量。若在 i i i处发现累加和小于0,则意味着 [ 0 , i ] [0,i] [0,i]之间的所有站点都不能作为出发站点,将start移至 i + 1 i+1 i+1

栗子:比如下表中列出每个站点的剩余油量。

0123456789
left-112-114-1042-4
sum-113237-3462

过程如下:

  1. 位置0不满足,start = 1
  2. 遍历到位置6时发现不满足:
    • start = 1不可以。
    • start = 2,由于left > 0到达位置6时,sum必然仍小于0。故同理,不能从left >= 0的位置出发。
    • start = 3left < 0,显然不能从此处出发。
  3. 可知start必在7之后,start = 7,继续检查。

如果总剩余量大于0,存在解。i从0开始,累加rest[i],和记为sum,一旦sum小于零,说明[0, i]区间都不能作为起始位置。

那么起始位置从i+1算起,再从0计算sum。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n = gas.length;
        int start = 0;
        int sum = 0;
        int totalSum = 0;
        for (int i = 0; i < n; i++) {
            sum += gas[i] - cost[i];
            totalSum += gas[i] - cost[i];
            if (sum < 0) {
                // sum清零,从i+1开始累计
                sum = 0;
                start = i + 1;
            }
        }
        return totalSum < 0 ? -1 : start;
    }
}

135 分发糖果★

真的,对贪心我属实只有一种办法,en记……

不过再做一遍应该就记得了,第一遍还是没啥思路了。

  • 两趟遍历,一趟由前向后,处理右>左的情况;另一趟在上一趟基础上,由后向前,处理左>右的情况。

  • 进阶TODO:如果是环形或者矩形呢?

import java.util.Arrays;

class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;
        int[] candys = new int[n];

        // 先给每个小朋友分一个~
        Arrays.fill(candys, 1);

        // 看看右边小朋友的分数是否比左边小朋友多,如果是的话,比左边的小朋友多分一个
        // 例子,分数:1 2 3 4 5 4 3 2 1
        // 糖数:1 1 1 1 1 1 1 1 1 -> 1 2 3 4 5 1 1 1 1
        for (int i = 1; i < n; i++) {
            if (ratings[i] > ratings[i - 1]) {
                candys[i] = candys[i - 1] + 1;
            }
        }

        // 看看左边小朋友的分数是否比右边小朋友多,如果是的话,判断是否左边小朋友的糖比右边小朋友多一个以上,如果不是,补上糖果
        // 倒着来,这样糖数可以累加
        // 例子,分数:1 2 3 4 5 4 3 2 1
        // 糖数:1 2 3 4 5 1 1 1 1 -> 1 2 3 4 5 4 3 2 1
        for (int i = n - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                candys[i] = Math.max(candys[i], candys[i + 1] + 1);
            }
        }

        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += candys[i];
        }
        return sum;
    }
}

860 柠檬水找零

模拟 + 贪心的简单题。

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

一开始你手头没有任何零钱。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

class Solution {
    public int money2index (int money) {
        if (money == 5) {
            return 0;
        }
        if (money == 10) {
            return 1;
        }
        return 2;
    }

    public boolean lemonadeChange(int[] bills) {
        int n = bills.length;
        // 使用一个长度为3的数组作为收银台
        int[] counter = new int[3];
        
        // 遍历每一位顾客的请求
        // 能够找零时先找大额钞票
        for (int i = 0; i < n; i++) {
            // 1、先拿进来这张钞票
            counter[money2index(bills[i])]++;
            // (1) 收到的是5元
            if (bills[i] == 5) {
                continue;
            }
            // (2) 10元
            if (bills[i] == 10) {
                // 如果没有5元的钞票,结束哩
                if (counter[0] <= 0) {
                    return false;
                }
                counter[0]--;
            }
            // (3)20元
            else {
                int charge = 15;
                // 如果有10元可以找,先把10元钱找掉
                if (counter[1] > 0) {
                    charge -= 10;
                    counter[1]--;
                }
                // 如果5元钞票不足
                if (counter[0] < charge / 5) {
                    return false;
                }
                // 否则找5元钞票
                counter[0] -= charge / 5;
            }
        }
        return true;
    }
}

406 根据身高重建队列★

【不仅做法要记一记,这题的语法也要记一记】

  • 每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好ki 个身高大于或等于 hi 的人。按照身高h递减,人数k递增的顺序排序
  • 向空ArrayList插入元素,people[i] = [hi, ki]插入位置ki

这个题解动画做得非常之形象:https://leetcode.cn/problems/queue-reconstruction-by-height/solutions/486493/xian-pai-xu-zai-cha-dui-dong-hua-yan-shi-suan-fa-g/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2lacErys-1686374064374)(【Leetcode】贪心问题合集/image-20230609213942723.png)]

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        // 按照h递减,k递增传递
        Arrays.sort(people, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[0] != o2[0]) {
                    // height更大的更优先
                    return o2[0] - o1[0];
                }
                // k更小的更优先
                return o1[1] - o2[1];
            }
        });

        ArrayList<int[]> queue = new ArrayList<>();

        for (int i = 0; i < people.length; i++) {
            int[] person = people[i];
            // 在位置k处插入
            queue.add(person[1], person);
        }

        return queue.toArray(new int[people.length][]);
    }
}

738 单调递增的数字★

思路:

  • 从右向左扫描数字,若发现当前数字比其左边一位(较高位)小
  • 则把其左边一位数字减1,并将该位及其右边的所有位改成9

例子:

10 -> 09
100 -> 1(-1)9 -> 099
990 -> 989 -> 899 

语法:代表数字的字符数组转为数字Integer.parseInt(String.valueOf(chars))

class Solution {
    public int monotoneIncreasingDigits(int n) {
        // 将数字转为字符数组
        String s = String.valueOf(n);
        char[] chars = s.toCharArray();

        int start = chars.length;

        for (int i = chars.length - 1; i >= 1; i--) {
            // 非递增序,需要修改
            // 前一位数字减1,后面的所有数字变成9
            // 但是不必这样反复进行变为9的操作,记录一下最后一个位置,把最后一个位置的所有数字变为9即可
            if (chars[i - 1] > chars[i]) {
                chars[i - 1]--;
                start = i;
            }
        }

        for (int i = start; i < s.length(); i++) {
            chars[i] = '9';
        }

        // 字符数组转为数字
        return Integer.parseInt(String.valueOf(chars));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/631094.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LED显示屏静电防护指南

LED显示屏是一种电子设备&#xff0c;对静电敏感。静电放电可能会对LED显示屏的电子元件造成损坏&#xff0c;因此需要采取静电防护措施。以下是LED显示屏静电防护的一些建议和指南&#xff1a; 环境控制&#xff1a;在LED显示屏周围创建适宜的环境条件。控制湿度和温度&#x…

Yolov5(tag v7.0)网络结构解读,以yolov5s为例

最近yolov5用的多&#xff0c;发现确实好用&#xff0c;于是较深入学了一下。下面按照训练的流程梳理一下网络的结构&#xff0c;同时也是自己记一下便于后面查阅。 同时&#xff0c;我也查了一些关于yolov5网络结构介绍的资料&#xff0c;发现大多是v5.0&#xff0c;少数v6.0的…

Linux驱动IO篇——阻塞/非阻塞IO

文章目录 非阻塞IO阻塞IO等待队列等待队列变体 非阻塞IO 在应用程序中&#xff0c;使用open函数打开一个/dev目录下的一个设备文件时&#xff0c;默认是以阻塞的方式打开。 所谓阻塞&#xff0c;就是当我们请求的资源不可用时&#xff08;资源被占用&#xff0c;没有数据到达等…

让车载系统与外部系统无缝对接——掌握SOA跨系统通信技术

车载SOA架构原理 车载 SOA&#xff08;Service-Oriented Architecture&#xff0c;面向服务的架构&#xff09;是一种基于服务的体系结构&#xff0c;旨在提高车载电子系统的可维护性、可扩展性和互操作性。它将车载电子系统划分为独立的、可复用的服务单元&#xff0c;这些服…

NodeJS MongoDB⑦

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言Node&MongoDB 第一步 连接数据库 第二步 创建User Mongodb模型 第三步 简单使用 Mongodb命令 第四步 规范使用 Mongodb命令 &#xff08…

爆款视频批量剪辑生成系统源码部署

创建视频 该接口用于创建抖音视频&#xff08;支持话题, 小程序等功能&#xff09;。该接口适用于抖音。 使用限制 抖音的 OAuth API 以https://open.douyin.com/ 开头。挂载小程序请先完成注册开发者平台账号。创建抖音视频后, 会有一个审核过程, 期间只有自己可见。如果发…

docker数据管理与网络通信

一、管理docker容器中数据 管理Docker 容器中数据主要有两种方式:数据卷(Data Volumes)和数据卷容器( DataVolumes Containers) 。 1、 数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xff0c;对数据卷的修改操作立刻…

leetcode11. 盛最多水的容器(java)

盛最多水的容器 leetcode11. 盛最多水的容器题目描述 解题思路代码演示二叉树专题 leetcode11. 盛最多水的容器 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/container-with-most-water 题目描述 给定一个长度为 n 的…

物联网Lora模块从入门到精通(二) LED灯泡闪烁与呼吸灯

目录 一、前言 二、实践与代码 1.电亮LED1 2.熄灭LED1 3.翻转LED电平 4.LED1与LED2交替闪烁 5.LED1呼吸灯 三、程序代码 一、前言 本篇内容属于新大陆物联网Lora模块开发&#xff0c;使用给定的Lora基础例程&#xff0c;并在其基础上开发完成&#xff0c;并可为其他版本的Lo…

Atcoder Beginner Contest 294

A - Filter AC代码&#xff1a; #include<iostream> #include<algorithm> #include<cstring> using namespace std; int main() {int n;cin>>n;for(int i0;i<n;i){int x;cin>>x;if(x%20)cout<<x<<" ";}return 0; } …

​​INNODB和MyISAM区别

1 存储引擎是MyISAM 如下&#xff1a; CREATE table test_myisam (cli int ) ENGINEMyISAM 存储目录里会有三个文件 test_myisam.frm为“表定义”&#xff0c;是描述数据表结构的文件 test_myisam.MYI文件是表的索引 test_myisam.MYD文件是表的数据 2 存储引擎是INNODB…

LeetCode——比较字符串最小字母出现频次

1、题目 1170. 比较字符串最小字母出现频次 - 力扣&#xff08;Leetcode&#xff09; 定义一个函数 f(s)&#xff0c;统计 s 中&#xff08;按字典序比较&#xff09;最小字母的出现频次 &#xff0c;其中 s 是一个非空字符串。 例如&#xff0c;若 s "dcce"&am…

httprunner 2.x的基本使用(一)

上一章&#xff1a; 下一章&#xff1a; httprunner 2.x的基本使用&#xff08;二&#xff09;_做测试的喵酱的博客-CSDN博客 一、参考地址&#xff1a; 使用说明_httprunner2.0 概述及使用说明 二、介绍 HttpRunner是一款面向 HTTP(S) 协议的通用测试框架&#xff0c;只需…

location.href 和 document.URL 与 document.documentURI

location.href 和 document.URL 与 document.documentURI 相同点 获取到的值相同 不同点 location.hrefurl可以赋值, 效果类似location.assign(url) , 可以后退 document.URL 与 document.documentURI 是只读的, 赋值无效 location.href locationwindow.location true lo…

从源码角度看Linux线程是怎么创建出来的

这篇文章来学习一下线程的创建过程。 线程不是一个完全由内核实现的机制&#xff0c;它是由内核态和用户态合作完成的。 用户态创建线程 pthread_create 不是一个系统调用&#xff0c;是 glibc 库的一个函数&#xff0c;位于 nptl/pthread_create.c 中&#xff1a; int __pth…

redis商户查询缓存

1 什么是缓存? 前言:什么是缓存? 就像自行车,越野车的避震器。 举个例子:越野车,山地自行车,都拥有"避震器",防止车体加速后因惯性,在酷似"U"字母的地形上飞跃,硬着陆导致的损害,像个弹簧一样; 同样,实际开发中,系统也需要"避震器",防止过…

Unity Shader - 兰伯特漫反射

兰伯特漫反射公式&#xff1a; 漫反射&#xff08;Diffuse&#xff09; 光源颜色 * max&#xff08;0&#xff0c;cos&#xff08;光方向和法线的夹角&#xff09;&#xff09; 公式原理&#xff1a; 从上面图片可以看出光照方向 L 与物体法相 N形成的 余弦值越大&#xff0c;反…

力扣笔记(每日随机一题)—— 打折购买糖果的最小开销

问题&#xff08;简单&#xff09; 一家商店正在打折销售糖果。每购买 两个 糖果&#xff0c;商店会 免费 送一个糖果。 免费送的糖果唯一的限制是&#xff1a;它的价格需要小于等于购买的两个糖果价格的 较小值 。 比方说&#xff0c;总共有 4 4 4 个糖果&#xff0c;价格…

开源代码分享(3)—微电网鲁棒定价策略(附matlab代码)

1背景介绍 1.1摘要 本论文聚焦于微电网中的能量失衡管理问题&#xff0c;并从电力市场的角度进行研究。与传统电力网不同&#xff0c;微电网可从可再生能源&#xff08;RES&#xff09;如太阳能电池板或风力涡轮机等获得额外能源。然而&#xff0c;来自RES的随机输入给平衡供需…

简述Vue的生命周期以及每个阶段做的事情

03_简述Vue的生命周期以及每个阶段做的事情 思路 给出概念 列举出生命周期各个阶段 阐述整体流程 结合实际 扩展&#xff1a;vue3变化 回答范例 每个vue组件实例被创建后都会经过一系列步骤。比如它需要数据观测、模板编译、挂载实例到dom、以及数据变化的时候更新dom、…