
代码随想录算法营||贪心算法
(2)添加弓箭:如果第i个的左边界大于前一个的右边界 if(point[i][0]>point[i-1][0]),因为两个气球连在一起也可以射。(3)如果两个气球重叠了,就会更新第i个气球的右边界Math.min(第i-1个气球的右边界,第i个气球的右边界)(1)如果第i个的左边界>第i-1个右边界,则不重叠;(1)如果是[1,2,2]则分发[1,2,1]个糖果,因为第二个2没有比第一个二高,所以
基础知识
1. 什么是贪心算法:通过找到局部最优找到全局最优
题目一:分发饼干
1. 题目:代码随想录 (programmercarl.com)
2. 思路:尽量用大饼干喂胃口大的小孩
- 先将两个数组进行排序 ,从大到小遍历数组小孩和饼干,小孩用for循环外层遍历,饼干用while循环内层遍历
- 为什么不能外层是饼干:
- 外面的 for 是里的下标 i 是固定移动的,而 if 里面的下标 index 是符合条件才移动的。
- 如果 for 控制的是饼干, if 控制胃口,就是出现如下情况 :
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int index=s.length-1;
int ans=0;
for(int i=g.length-1;i>=0;i--){//遍历胃口
if(index>=0 && s[index]>=g[i]){
index--;
ans++;
}
}
return ans;
}
}
题目二:摆动序列
1. 题目:376. 摆动序列 - 力扣(LeetCode)
2. 思路:局部最优是单调坡里的元素都删除掉,全局最优是得到摆动最长的序列
(1)因为输出的是长度,所以不需要输出摆动子序列,遇到摆动就++
(2)判断摆动:prediff=nums[i]-nums[i]-1;curdiff=nums[i+1]-nums[i]是一正一负
(3)上下坡有平坡:例如1,2,2,2,1的最大摆动序列长度是3
class Solution {
public int wiggleMaxLength(int[] nums) {
if (nums.length <= 1)return nums.length;
//当前差值
int curDiff = 0;
//上一个差值
int preDiff = 0;
int count = 1;
for (int i = 1; i < nums.length; i++) {
//得到当前差值
curDiff = nums[i] - nums[i - 1];
//如果当前差值和上一个差值为一正一负
//等于0的情况表示初始时的preDiff
if ((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0)) {
count++;
preDiff = curDiff;
}
}
return count;
}
}
题目三:最大子序和
1. 题目:53. 最大子数组和 - 力扣(LeetCode)
2. 思路:局部最优是如果和为负数则删掉,全局最优是找到最大连续子数组的和
(1) 先将result更新,再判断if(sum<0)sum=0
class Solution {
public int maxSubArray(int[] nums) {
public int maxSubArray(int[] nums) {
if(nums.length==1)return nums[0];
int sum=0;
int result=Integer.MIN_VALUE;
for (int i=0;i<nums.length;i++){
sum+=nums[i];
result=Math.max(result,sum);
if (sum<=0)sum=0;
}
return result;
}
}
题目四:买卖股票的最佳时机||
1. 题目:122. 买卖股票的最佳时机 II - 力扣(LeetCode)
2. 思路:
(1)局部最优是遇到利润为正数就加起来,全局最优是最后收集到了最大利润
public int maxProfit(int[] prices) {
int ans=0;
for (int i=1;i<prices.length;i++){
if (prices[i]-prices[i-1]>0)ans+=prices[i]-prices[i-1];
}
return ans;
}
题目五:跳跃游戏
1. 题目:55. 跳跃游戏 - 力扣(LeetCode)
2. 思路:看能不能覆盖到最后的位置,for循环是遍历已覆盖的范围,不是全部的数组
public boolean canJump(int[] nums) {
int cover=0;
for (int i=0;i<=cover;i++){
cover=Math.max(i+nums[i],cover);
if (cover>=nums.length-1)return true;
}
return false;
}
题目六:跳跃游戏
1. 题目:45. 跳跃游戏 II - 力扣(LeetCode)
2. 思路:每次都找到,下一步的覆盖范围
public int jump(int[] nums) {
int result = 0;
// 当前覆盖的最远距离下标
int end = 0;
// 下一步覆盖的最远距离下标
int temp = 0;
for (int i = 0; i <= end && end < nums.length - 1; ++i) {
temp = Math.max(temp, i + nums[i]);
// 可达位置的改变次数就是跳跃次数
if (i == end) {
end = temp;
result++;
}
}
return result;
}
题目七:K次取反后最大化的数组和
1. 题目:1005. K 次取反后最大化的数组和 - 力扣(LeetCode)
2. 思路
(1)第一次贪心:将所有的负数转化为整数,优先绝对值大的数
(2)第二次贪心:都转化为正数之后,选取最小的正数取反直到消耗完k
(3)按照绝对值的大小进行排序
- 是负数:if(nums[i<0 && k<0])nums*=-1;k--
- 都取完之后:如果k是奇数则对最小的数取反一次,如果是偶数不用进行操作
public int largestSumAfterKNegations(int[] nums, int k) {
Arrays.sort(nums);//1.排序数组,将数组中负数转化为正数
for (int i=0;i<nums.length && k>0;i++){
if (nums[i]<0){
nums[i]=-nums[i];
k--;
}
}
//2.剩余k如果是奇数则重新排序,取最小的取反
if (k%2!=0){
Arrays.sort(nums);
nums[0]*=-1;
}
//3.数组中所有元素相加,得到ans
int ans=0;
for (int i:nums){
ans+=i;
}
return ans;
}
题目八:加油站
1. 题目:134. 加油站 - 力扣(LeetCode)
2. 思路:(1)先用gas-cost计算剩余的油量,在一个for循环里计算totalsum和cursum;如果cursum<0则遍历下一个站点;如果totalsum<0则一定没有答案
int cursum=0,totalsum=0,ans=0;
for (int i=0;i<gas.length;i++){
cursum+=gas[i]-cost[i];
totalsum+=gas[i]-cost[i];
if (cursum<0){
ans=i+1;
cursum=0;
}
}
if (totalsum<0)return -1;
return ans;
题目九:分发糖果
1. 题目:135. 分发糖果 - 力扣(LeetCode) :每个孩子至少分配到 1 个糖果;相邻的孩子中,评分高的孩子必须获得更多的糖果。
2. 思路:只比较一边,不比较两边
(1)如果是[1,2,2]则分发[1,2,1]个糖果,因为第二个2没有比第一个二高,所以就发一个
(2)右孩比左孩评分高,从前向后遍历
(3)左孩比右孩评分高,从后向前遍历
class Solution {
public int candy(int[] ratings) {
//1、起点下标1 从左往右,只要 右边 比 左边 大,右边的糖果=左边 + 1
int[] candy1=new int[ratings.length];
candy1[0]=1;
int res=0;
for (int i=1;i<candy1.length;i++){
if (ratings[i]>ratings[i-1])candy1[i]=candy1[i-1]+1;
else candy1[i]=1;
}
//2、起点下标 ratings.length - 2 从右往左, 只要左边 比 右边 大,
// 左边的糖果应该 取本身的糖果数(符合比它左边大) 和 右边糖果数 + 1 二者的最大值
for (int i=candy1.length-2;i>=0;i--){
if (ratings[i]>ratings[i+1]){
candy1[i]=Math.max(candy1[i],candy1[i+1]+1);
}
}
//3.计算和
for(int i:candy1){
res+=i;
}
return res;
}
}
题目十:柠檬水找零
1. 题目:860. 柠檬水找零 - 力扣(LeetCode)
class Solution {
public boolean lemonadeChange(int[] bills) {
int five=0;
int ten=0;
for (int i=0;i<bills.length;i++){
if (bills[i]==5)five++;
else if (bills[i]==10){
ten++;
five--;
}else if (bills[i]==20){
if (ten>0){
ten--;
five--;
}else{
five-=3;
}
}
if (five<0||ten<0)return false;
}
return true;
}
}
题目十一:根据身高排队列
1. 题目:406. 根据身高重建队列 - 力扣(LeetCode)
2. 思路:有两个排序条件的时候一个一个来
(1)先按照k进行排序,再按照身高进行排序不太行
(2)采用先按照身高从大到小排序,排序的时候根据k进行调整:用排序方法的时候升序是a-b,降序是b-a
public int[][] reconstructQueue(int[][] people) {
//1.先将people进行排序
Arrays.sort(people,(a,b) ->{//people数组中的a,b
if (a[0]==b[0])return a[1]-b[1];//如果身高相等,则升序看k值
return b[0]-a[0];//如果身高不等,降序看身高
}
);
LinkedList<int[]> que=new LinkedList<>();//linkedlist方便增加删除
for (int i=0;i<people.length;i++){
que.add(people[i][1],people[i]);//按照他的k进行排序
}
return que.toArray(new int[people.length][]);
}
题目十二:用最少数量的箭引爆气球
1. 题目:452. 用最少数量的箭引爆气球 - 力扣(LeetCode)
2. 思路:只需要记录几只箭不需要把气球从数组中移除
(1)按照左边界进行排序;比较的是当前气球和前一个气球,所以遍历从1开始
(2)添加弓箭:如果第i个的左边界大于前一个的右边界 if(point[i][0]>point[i-1][0]),因为两个气球连在一起也可以射
(3)如果两个气球重叠了,就会更新第i个气球的右边界Math.min(第i-1个气球的右边界,第i个气球的右边界)
public int findMinArrowShots(int[][] points) {
Arrays.sort(points,(a,b)->Integer.compare(a[0],b[0]));
int ans=1;
for (int i=1;i<points.length;i++){
//1.比较i的左边界和i-1的右边界
if (points[i][0]>points[i-1][1])ans++;
else {//2.比较右边界
points[i][1]=Math.min(points[i-1][1],points[i][1]);
}
}
return ans;
}
题目十三:无重叠区间
1. 题目:. - 力扣(LeetCode)
2. 思路:判断有几个重叠区间删除之后就是无重叠区间了;左边界排序
(1)如果第i个的左边界>第i-1个右边界,则不重叠;若重叠,则取i和i-1右边界的最小值,在和下一个进行比较
public int eraseOverlapIntervals(int[][] intervals) {
//1.对数组进行排序
Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
int ans=0;
for(int i=1;i<intervals.length;i++){//从1开始找重叠区间
if(intervals[i][0]<intervals[i-1][1]){
ans++;
intervals[i][1]=Math.min(intervals[i-1][1],intervals[i][1]);
}
}
return ans;
}
题目十四:划分字母区间
1. 题目:763. 划分字母区间 - 力扣(LeetCode)
2. 先遍历一遍字符串,找到每个字母最远的位置,保存到数组中,然后遍历字符串
public List<Integer> partitionLabels(String s) {
List<Integer> ans=new ArrayList<>();
int[] farest=new int[26];
char[] chars=s.toCharArray();
for (int i=0;i<s.length();i++){
farest[chars[i]-'a']=i;
}
int left=0,right=0;
for (int i=0;i<s.length();i++){
right=Math.max(right,farest[chars[i]-'a']);
if (i==right){
ans.add(right-left+1);
left=i+1;
}
}
return ans;
}
题目十五:合并区间
1. 题目:56. 合并区间 - 力扣(LeetCode)
public int[][] merge(int[][] intervals) {
//1.对intervals进行排序按照第一个数字、升序
Arrays.sort(intervals,(a,b)-> Integer.compare(a[0],b[0]));
List<int[]> ans=new ArrayList<>();
ans.add(intervals[0]);//2.先插入第一个数字
for (int i=1;i<intervals.length;i++){//从后面的数字开始
int lastright=(ans.getLast())[1];
int lastleft=(ans.getLast())[0];
if (intervals[i][0]<=lastright){//3.需要更新数组
ans.removeLast();//将最后一个取出
//最右进行比较,取大的
ans.add(new int[]{lastleft,Math.max(intervals[i][1],lastright)});
}else{//不需要更新,直接插入第i个数组
ans.add(new int[]{intervals[i][0],intervals[i][1]});
}
}
return ans.toArray(new int[ans.size()][]);//转化成array
}
题目十六:单调递增的数字
1. 题目:738. 单调递增的数字 - 力扣(LeetCode)
2. 思路:
(1)332,从前向后遍历的话,那么就把变成了329,此时2又小于了第一位的3了,真正的结果应该是299。那么从后向前遍历,就可以重复利用上次比较得出的结果了,从后向前遍历332的数值变化为:332 -> 329 -> 299
(2)因为在遍历的过程中要用到i-1,所以for循环的边界条件是i>0
(3)flag的用途是记录从哪一位开始变成9
public int monotoneIncreasingDigits(int n) {
String s= String.valueOf(n);
char[] num=s.toCharArray();
int flag=num.length;
for (int i=num.length-1;i>0;i--){
if (num[i]<num[i-1]){
num[i-1]--;
flag=i;
}
}
for (int i=flag;i<num.length;i++){
num[i]='9';
}
return Integer.parseInt(String.valueOf(num));
}
更多推荐
所有评论(0)