贪心算法

分发饼干

leetcode题目链接

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

思路草稿

分发饼干

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int count = 0;

for(int i = 0,j = 0;i < s.length && j < g.length;i++){
if(s[i]>=g[j]){
count++;
j++;
}
}

return count;
}
}

摆动序列

leetcode题目链接

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

例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

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

思路草稿

摆动序列

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int wiggleMaxLength(int[] nums) {
if(nums.length<=1){
return nums.length;
}

int prediff = 0;
int curdiff = 0;
int result = 1;

for(int i = 0;i<nums.length-1;i++){
curdiff = nums[i+1]-nums[i];

if(prediff>=0&&curdiff<0||prediff<=0&&curdiff>0){
result++;
prediff = curdiff;
}
}

return result;
}
}

最大子数组和

leetcode题目链接

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

思路草稿

最大子数组和

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int maxSubArray(int[] nums) {
if(nums.length==1){
return nums[0];
}

int count = 0;
int result = Integer.MIN_VALUE;

for(int i = 0;i<nums.length;i++){
count += nums[i];
result = Math.max(count,result); // 取最大子数组和

if(count<=0){ // 当子数组和为负数,直接重置,从下一个元素重新计数
count=0;
}
}

return result;
}
}

买卖股票的最佳时机II

leetcode题目链接

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 。

思路草稿

买卖股票的最佳时机II

题解

1
2
3
4
5
6
7
8
9
class Solution {
public int maxProfit(int[] prices) {
int result = 0;
for(int i = 1;i<prices.length;i++){
result += Math.max(prices[i]-prices[i-1],0);
}
return result;
}
}

跳跃游戏

leetcode题目链接

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

思路草稿

跳跃游戏

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public boolean canJump(int[] nums) {
if(nums.length==1){
return true;
}

int size = 0;
for(int i = 0;i<=size;i++){ // size 一直增大
size = Math.max(i+nums[i],size); // i+nums[i] 当前位置加上偏移量,即能到达的最大位置

if(size >= nums.length-1){
return true;
}
}
return false;
}
}

跳跃游戏II

leetcode题目链接

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。

思路草稿

跳跃游戏II

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public int jump(int[] nums) {
if(nums.length==1){
return 0;
}

int cur = 0;
int next = 0;
int step = 0;
for(int i = 0;i<nums.length;i++){
next = Math.max(i+nums[i],next); // 下一步的最远距离

// tips:先确定下一步是否能到达
// 否则如果当前正好在最远的位置上,且下一步能到达最后一个元素,就会出现多加一步的情况

if(next>=nums.length-1){ // 下一步就能到达最后一个元素
step++;
break;
}

if(cur==i){ // 已经到达当前的最远距离,步数+1,进入下一步并更新当前最远距离
step++;
cur = next;
}
}
return step;
}
}

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

leetcode题目链接

给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:

选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。
重复这个过程恰好 k 次。可以多次选择同一个下标 i 。

以这种方式修改数组后,返回数组 可能的最大和 。

思路草稿

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

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
Arrays.sort(nums); // 排序,将负数放在数组前面
int sum = 0;
for(int i = 0;i<nums.length;i++){
if(nums[i]<0&&k>0){
nums[i]=-nums[i];
k--;
}
sum+=nums[i];
}
Arrays.sort(nums); // 排序,负数取反后再次排序

// 如果k没剩,说明负数已经尽可能取反完成,当前sum就是最大和
// 如果k有剩,说明剩下的全是正数
// 如果k 是偶数,偶数次取反仍为正,可以直接认为 当前 sum 就是最大和
// 如果k 是奇数,sum 需要减去 最小正数nums[0]的两倍
// 因为之前sum加上了nums[0],减去一次只相当于恢复原值(没有加上nums[0),还需再减一次,才可视为加上取反之后的值
return sum - (k%2==0 ? 0 : 2*nums[0]);
}
}

加油站

leetcode题目链接

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

思路草稿

加油站

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int curSum = 0; // 记录从start开始每次剩余的油量
int totalSum = 0; // 记录总共剩余的油量
int start = 0;// 起始位置

for(int i = 0;i<gas.length;i++){
curSum += gas[i]-cost[i];
totalSum += gas[i]-cost[i];
if(curSum<0){ // 如果当前剩余油量<0,说明之前的位置都不能绕路一圈,选择下一个位置i+1作为起始位置
start=i+1;
curSum=0;
}
}
if(totalSum<0){ // 如果总剩余油量小于0,说明怎么都走不了一圈
return -1;
}
return start;
}
}

分发糖果

leetcode题目链接

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

思路草稿

分发糖果

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public int candy(int[] ratings) {
int[] candy = new int[ratings.length];
int result = 0;
Arrays.fill(candy,1);

for(int i = 1;i<ratings.length;i++){ // 1.右边孩子分数更高
if(ratings[i]>ratings[i-1]){
candy[i]=candy[i-1]+1;
}
}

for(int i = ratings.length-2;i>=0;i--){ // 2.左边孩子分数更高
if(ratings[i]>ratings[i+1]){
candy[i] = Math.max(candy[i+1]+1,candy[i]); // 取两种情况的最大值,两者兼顾
}
}

for(int i = 0;i<candy.length;i++){
result += candy[i];
}

return result;
}
}

柠檬水找零

leetcode题目链接

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

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

注意,一开始你手头没有任何零钱。

给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

思路草稿

柠檬水找零

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public boolean lemonadeChange(int[] bills) {
int five = 0,ten = 0,twenty = 0;
for(int bill:bills){
if(bill==5){
five++;
}

if(bill==10){
if(five>0){
five--;
ten++;
} else {
return false;
}
}

if(bill==20){
if(ten>0&&five>0){
ten--;
five--;
twenty++;
} else if(five>=3){
five-=3;
twenty++;
} else {
return false;
}
}
}
return true;
}
}

根据身高重建队列

leetcode题目链接

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

思路草稿

根据身高重建队列

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people,(a,b)->{
if(a[0]==b[0]){ // 身高相同
return a[1]-b[1]; // -1 k从小到大
}
return b[0]-a[0]; // 1 身高从大到小
});

List<int[]> list = new ArrayList<>();

for(int[] p : people){
list.add(p[1],p); // 指定k位置插入列表
}

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

用最少数量的箭引爆气球

leetcode题目链接

有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。

给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。

思路草稿

用最少数量的箭引爆气球

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int findMinArrowShots(int[][] points) {
if(points.length==0){
return 0;
}
int result = 1;
Arrays.sort(points,(a,b)->Integer.compare(a[0],b[0]));

for(int i = 1;i<points.length;i++){
if(points[i][0]>points[i-1][1]){ // 当前左大于前一右,两个气球不在同一范围
result++;
} else { // 当前左小于等于前一右,两个气球在同一范围
// 更新当前右为两者右的最小值
// 保证气球在同一范围
points[i][1]=Math.min(points[i][1],points[i-1][1]);
}
}

return result;
}
}