分发饼干
链接: 455.分发饼干
思路
用大饼干满足胃口尽可能大的小孩
对饼干和胃口两个数组排序之后,选择大饼干,因为是胃口尽可能大,所以我们从大到小遍历胃口,直到遍历到符合条件的大胃口。count++后,我们的start–,到了次大的饼干,接着刚才的遍历继续遍历。
代码
class Solution {
public int findContentChildren(int[] g, int[] s) {
if (s.length == 0) return s.length;
Arrays.sort(g);
Arrays.sort(s);
int count = 0;
int start = s.length - 1;
for (int i = g.length - 1;i >= 0;i--){
if (start >= 0 && g[i] <= s[start]){
count++;
start--;
}
}
return count;
}
}
摆动序列
链接: 376. 摆动序列
思路
贪心算法就是把思路简单化,因为是求摆动序列的长度,因此我们不必将代码里面不符合条件的元素删除,只需要忽略他们即可。
把序列按照大小画成有高低起伏的样子,需要重点考虑以下三种情况:
考虑三种情况:
- 上下有平坡
- 首尾元素
- 单调有平坡
上下有平坡:
if ((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0))
上下变化即可
首尾元素的话,主要是考虑到,“仅有一个元素或者含两个不等元素的序列也视作摆动序列。”条件
单独一个元素,我们将之单独写出来
两个不等元素,我们可以假设第一个元素前还有一个平坡,preDiff=0,count=1
单调有平坡:
for (int i = 1;i < nums.length;i++){
curDiff = nums[i] - nums[i-1];
if ((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0)){
count++;
preDiff = curDiff;
}
}
将preDiff = curDiff;放入if中,只有当坡度有变化时,才改变preDiff的值
【本题的思路是我想不到又有一点理解不了的,要多看】
代码
class Solution {
public int wiggleMaxLength(int[] nums) {
if (nums.length == 0 || nums.length == 1) return nums.length;
int preDiff = 0;
int curDiff = 0;
int count = 1;
for (int i = 1;i < nums.length;i++){
curDiff = nums[i] - nums[i-1];
if ((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0)){
count++;
preDiff = curDiff;
}
}
return count;
}
}
最大子序和
链接: 53. 最大子序和
思路
贪心算法类的题目有点像脑筋急转弯
本题主要是思考全面一点
不要用脑子想,你的脑子又不是很好使
写一些典型事例,考虑全面一点(这个是你需要训练的)
从一般题目抽象出一般规律,用代码表现出来,是一种建模能力。
多大胆假设,然后尝试用代码实现假设
注意点
1.sum = Integer.MIN_VALUE
我一开始想sum = 0;但是题目中有负数的情况,max(0,0+负数)肯定会选0,不可以。
2.我原来在判断时max(sum,sum+nums[i]),不可以,如果这样,则没办法舍弃原来的序列,构造新的序列,所以需要再设置一个count=0,来记录每一次连续子序列的和,不符合条件(count < 0),就让count = 0,重新开始记录新的序列
而sum = max(sum,count),sum 则是记录当前连续子序列最大和
3.count < 0,
count < 0时,count + nums[i] < nums[i] 我是求最大和,不是求最大长度,所以不如从nuns[i]开始重新记录一个新序列
代码
class Solution {
public int maxSubArray(int[] nums) {
int totalSum = Integer.MIN_VALUE;
int curSum = 0;
for (int i = 0;i < nums.length;i++){
curSum += nums[i];
totalSum = Math.max(totalSum,curSum);
if (curSum < 0) curSum = 0;
}
return totalSum;
}
}
买卖股票的最佳时机Il
链接: 122.买卖股票的最佳时机II
思路
股票买卖nums[3] - nums[1],可以写成连加的形式nums[3]-nums[2] + nums[2] - nums[1] + nums[1] - nums[0]
所以本题求出该数组相邻元素的差值,选择均是正数的几天卖出(相加)
代码
class Solution {
public int maxProfit(int[] prices) {
if (prices.length == 1) return 0;
int result = 0;
for (int i = 1;i < prices.length;i++){
if (prices[i] - prices[i - 1] > 0){
result += (prices[i] - prices[i - 1]);
}
}
return result;
}
}
跳跃游戏
链接: 55. 跳跃游戏
思路
题目和思路都挺简单的,主要是细节的处理
最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
那么我们遍历的范围应该是【0,coverRange】
自己的脑筋不够灵活,写成了
for (int i = 0;i <= nums.length;i++)
再三报错
一定要认真审题,审完题后在草稿纸上写一下过程,然后脑袋灵活一点建模,不可以全部凭借回忆。
代码
class Solution {
public boolean canJump(int[] nums) {
if (nums.length == 1) return true;
int cover = 0;
for (int i = 0;i <= cover;i++){
cover = Math.max(nums[i] + i,cover);
if (cover >= nums.length - 1){
return true;
}
}
return false;
}
}
跳跃游戏II
链接: 45.跳跃游戏II
思路
代码解析:
这种一般都是找一个接一下最大的范围
然后有个判断终止条件,其余的根据题意多设几个变量,灵活一点呗
代码
class Solution {
public int jump(int[] nums) {
if (nums.length == 1) return 0;
int curCover = 0;
int maxCover = 0;
int count = 0;
for (int i = 0;i < nums.length;i++){
maxCover = Math.max(maxCover,nums[i] + i);
if (maxCover >= nums.length - 1){
count++;
break;
}
if (i == curCover){
curCover = maxCover;
count++;
}
}
return count;
}
}
K次取反后最大化的数组和
思路
本题是简单题目,因此用自己的传统思路解决了该问题
自己的思路
排序,排完序后将小于0的先全部变成大于0,然后统计还能取反的次数。
如果k值还有剩余,那么说明数组中的所有数值都应该是大于等于0的
取反后数组不再是有序的,再次排序,
如果剩余k值为偶数,相当于不变
如果k为奇数
选择第一个元素(绝对值最小的元素),将其取反
然后遍历数组元素,相加得到最后结果
官方思路
官方思路更清晰一点
那么本题的解题步骤为:
- 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
- 第二步:从前向后遍历,遇到负数将其变为正数,同时K–
- 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
- 第四步:求和
但是转换数据类型看起来很麻烦
代码
自己思路的代码
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
Arrays.sort(nums);
for (int i = 0;i < nums.length && k > 0;i++){
if (nums[i] < 0){
nums[i] = - nums[i];
k--;
}
}
Arrays.sort(nums);
if (k % 2 != 0) nums[0] = -nums[0];
int sum = 0;
for (int i = 0;i < nums.length;i++){
sum += nums[i];
}
return sum;
}
}
别人思路的代码
//绝对值
class Solution {
public int largestSumAfterKNegations(int[] nums, int K) {
// 将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
nums = IntStream.of(nums)
.boxed()
.sorted((o1, o2) -> Math.abs(o2) - Math.abs(o1))
.mapToInt(Integer::intValue).toArray();
int len = nums.length;
for (int i = 0; i < len; i++) {
//从前向后遍历,遇到负数将其变为正数,同时K--
if (nums[i] < 0 && K > 0) {
nums[i] = -nums[i];
K--;
}
}
// 如果K还大于0,那么反复转变数值最小的元素,将K用完
if (K % 2 == 1) nums[len - 1] = -nums[len - 1];
return Arrays.stream(nums).sum();
}
}
另一种思路
//有点绕,不太想看
class Solution {
public int largestSumAfterKNegations(int[] A, int K) {
if (A.length == 1) return k % 2 == 0 ? A[0] : -A[0];
Arrays.sort(A);
int sum = 0;
int idx = 0;
for (int i = 0; i < K; i++) {
if (i < A.length - 1 && A[idx] < 0) {
A[idx] = -A[idx];
if (A[idx] >= Math.abs(A[idx + 1])) idx++;
continue;
}
A[idx] = -A[idx];
}
for (int i = 0; i < A.length; i++) {
sum += A[i];
}
return sum;
}
}
加油站
链接: 134. 加油站
思路
相信自己的思路,在草稿纸上思维敏捷地想一想,不要总是否定自己
本题看起来有些复杂
例如从索引3出发到达索引4,得到4升油,消耗1升,最后剩余3升
自己一开始的思路总是异常直白,总是思考环状,很难去实现。
观察一下发现,gas[i] - cost[i] < 0时,则到达不了。
因此我们的思路是:
遍历数组,求从该索引开始环状遍历相加所求之和大于0的索引。
设置一个当前和curSum,这个是一直在变化的
设置一个总的和totalSum,这个一直遍历相加
设置一个我们的符合条件的索引 Index = 0;
遍历数组相加,如果 < 0,则curSum清零,重新统计新的索引开始之和;index = i + 1;(当前索引不符合条件,我们返回下一个索引嘛)
我之前没想明白否定自己思路的一点在于,如果从中间结点开始,要环状遍历一遍,我不知道该如何实现。
本题不需要考虑环状,前面遍历的节点不符合条件,保证开始索引之后的节点符合条件,这时候只要totalSum >=0就说明符合(在代码中 是 < 0 返回-1;否则直接return Index)
代码
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int totalSum = 0;
int curSum = 0;
int index = 0;
for (int i = 0;i < gas.length;i++){
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if (curSum < 0){
curSum = 0;
index = i + 1;
}
}
if (totalSum < 0) return -1;
return index;
}
}
分发糖果
链接: 135. 分发糖果
思路
本题思路自己比较模糊,想了像摆动序列一样,写成上下波动一样,但是自己在思考时总是漏一些情况。
本题两个条件:
- 每个孩子至少分配到 1 个糖果。
- 相邻的孩子中,评分高的孩子必须获得更多的糖果。
设置nums数组,记录第i个孩子能获得的糖果数量
条件一应将nums中的每个元素都赋值为1
然后从左向右遍历,如果左元素 < 右元素,则右元素的糖果数量比左元素的多一个;
然后从右向左遍历,如果左元素 > 右元素,则左元素的糖果数量应该在右元素的基础上+1,但有可能在上一个遍历上,左元素的糖果数量已经很大了,因此,我们用Math.max取最大。
遍历相加,即可
代码
class Solution {
public int candy(int[] ratings) {
int[] nums = new int[ratings.length];
nums[0] = 1;
for (int i = 1;i < ratings.length;i++){
nums[i] = 1;
if (ratings[i] > ratings[i - 1])
nums[i] = nums[i - 1] + 1;
}
for (int i = ratings.length - 2;i >= 0;i--){
if (ratings[i] > ratings[i + 1])
// 还是要落到纸上写一写
nums[i] = Math.max(nums[i + 1] + 1,nums[i]);
}
int sum = 0;
for (int i = 0;i < ratings.length;i++){
sum += nums[i];
}
return sum;
}
}