贪心算法

分发饼干

题目描述

有两个数组,一个代表小孩,一个代表饼干,第$i$个小孩的值是饥饿度,第$i$个饼干是饱和度,请问,饼干最多能让多少小孩吃饱。

解题思路

小饼干先喂饱小胃口。下面的代码中利用遍历cookie来遍历胃口数组,并没有再使用一个for循环,而是采用自加的方式,这也是常用的技巧。

public static int findContentChildren(int[]cookies, int[]children) {
// 首先进行排序
Arrays.sort(cookies);
Arrays.sort(children);
int child = 0;
int cookie = 0;
// 从小到大进行遍历,最小大饼干分给最小的胃口
while (child < children.length && cookie < cookies.length) {
if (children[child] <= cookies[cookie++])
child++;
}
return child;
}

摆动序列

题目描述

给定一个整数序列,返回作为摆动序列的最长子序列的长度。通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

解题思路

局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值。

整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列。

实际操作上,其实连删除的操作都不用做,因为题目要求的是最长摆动序列的长度,所以只需要统计数组的峰值数量就可以了。

public static int wiggleMaxLength(int[] nums) {
if (nums.length <= 1) {
return nums.length;
}
int result = 1;
int pre = 0;
int cur = 0;
for (int i = 1; i < nums.length; i++) {
cur = nums[i] - nums[i-1];
if (cur > 0 && pre <= 0 || cur < 0 && pre >= 0) {
result++;
pre = cur;
}
}
return result;
}

最大子序和

题目描述

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

解题思路

局部最优:当前连续和为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素的连续和只会越来越小。

全局最优:选取最大“连续和”。

public static int Maximum_Suborder_Sum(int[] nums) {
if (nums.length == 1) {
return nums[0];
}
int count = 0;
int max = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
count += nums[i];
// 取区间累计的最大值(相当于不断确定最大子序终止为止)
max = Math.max(max, count);
if (count < 0){
// 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
count = 0;
}
}
return 0;
}

买卖股票的最佳时机II

题目描述

给定一个数组,它的第i个元素是一支给定股票第i天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)

解题思路

其实只需要收集每天的正利润就可以了,收集正利润的区间,就是股票买卖的区间,而只需要关注最终利润,不需要记录区间。

局部最优:收集每天的正利润,全局最优:求得最大利润。

public static int maxProfit(int[] prices) {

int max = 0; // 最大利润
int flag = 0; // 是否持有,0没有,1买入
int cur = 0;
for (int i = 1; i < prices.length; i++) {
cur = prices[i] - prices[i-1];
if (cur > 0) {
max += cur;
flag = 1;
}
if (cur < 0 && flag == 1) {
flag = 0;
}
// 优化
// max += Math.max(prices[i] - prices[i - 1],0);
}
return max;
}