算法日记 26-27day 贪心算法

接下来的题目有些地方比较相似。需要注意多个条件。

题目:分发糖果

135. 分发糖果 - 力扣(LeetCode)

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

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

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目

题目分析:

分发糖果需要考虑左右两边的大小,不能同时考虑,会很乱。那么就先考虑一边,在考虑另外一边。

那么就变成了,从前向后看,比较右孩子大于左孩子的情况和从后往前看,比较左孩子大于右孩子的情况。之所以第二个条件需要从后往前的原因,是因为继续从前往后会不满足题目。

就像这样

所以需要从后往前

代码如下:

cs 复制代码
public class Solution {
    public int Candy(int[] ratings) {
        int[] res=new int[ratings.Length];
        int candys=0;
        for(int i=0;i<res.Length;i++){
            res[i]=1;//所有孩子最低都是1
        }
        //因为比较需要考虑左右两边的情况,所以需要分开考虑(两个条件)
        for(int i=1;i<ratings.Length;i++){//从前向后看,比较右孩子大于左孩子的情况
            if(ratings[i]>ratings[i-1]){
                res[i]=res[i-1]+1;
            }
        }
        for(int i=ratings.Length-2;i>=0;i--){//从后往前看,比较左孩子大于右孩子的情况
            if(ratings[i]>ratings[i+1]){
                res[i]=Math.Max(res[i],res[i+1]+1);
            }
        }
        for(int i=0;i<res.Length;i++){
            candys+=res[i];
        }
        return candys;
    }
}

题目:根据身高重建队列

406. 根据身高重建队列 - 力扣(LeetCode)

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

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

题目分析:

同样的需要满足两个条件,那就一个一个的考虑。我们根据身高进行从大到小的排列,身高相同的就根据k进行从小到达的排列。至于为什么这样排,结合题目要求来看

那么代码就很好写出来了

cs 复制代码
public class Solution {
    public int[][] ReconstructQueue(int[][] people) {
        //需要考虑两个条件,分开考虑
        //先根据身高从大到小排序,这样插入的时候不用考虑K,
        //因为排序之后这个值后面的值一定比他小,在前面插入小的值,自然影响不到他
        Array.Sort(people,(a,b)=>{
            if(a[0]==b[0]){//同样的身高,根据k排序,从小到大
                return a[1]-b[1];
            }
            else return b[0]-a[0];
        });

        var res = new List<int[]>();
        for(int i=0;i<people.Length;i++){//根据k直接插入
            res.Insert(people[i][1],people[i]);
        }
        return res.ToArray();
    }
}

题目:用最少数量的箭引爆气球

452. 用最少数量的箭引爆气球 - 力扣(LeetCode)

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

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

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

题目分析:

根据题目的意思,气球的摆放是有重叠的,如何使用更少的弓箭,自然就是从重叠下手。

就像这样

看看代码理解

cs 复制代码
public class Solution {
    public int FindMinArrowShots(int[][] points) {
        Array.Sort(points,(a,b)=>a[0].CompareTo(b[0]));
        int res=1;
        for(int i=1;i<points.Length;i++){
            if(points[i][0]>points[i-1][1])//后面气球的起点超过了这个气球的终点,气球不相交
                res++;
            else{//气球重贴了,就把两个气球的范围合起来
                points[i][1]=Math.Min(points[i-1][1],points[i][1]);// 更新重叠气球最小右边界
            }
        }
        return res;
    }
}

题目分析:无重叠区间

435. 无重叠区间 - 力扣(LeetCode)

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠

注意 只在一点上接触的区间是 不重叠的 。例如 [1, 2][2, 3] 是不重叠的。

题目分析:

和上一题其实差不多的,那么排序的时候我们选择左边界排序还是右边界排序呢?其实都是没有问题的,

假如我们根据有边界排序,从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了。

如果根据左边界排序,就是直接求 重叠的区间,count为记录重叠区间数

cs 复制代码
public class Solution {
    public int EraseOverlapIntervals(int[][] intervals) {
        if (intervals.Length == 0) return 0;
        Array.Sort(intervals, (a, b) => a[1].CompareTo(b[1]));
        int res = 1, end = intervals[0][1];
        for (int i = 1; i < intervals.Length; i++)
        {
            if (end <= intervals[i][0])
            {
                end = intervals[i][1];
                res++;
            }
        }
        return intervals.Length - res;
    }
}

题目:划分字母区间

763. 划分字母区间 - 力扣(LeetCode)

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s

返回一个表示每个字符串片段的长度的列表。

题目分析:

这一题可以使用hash去统计字符的位置,但是这样很容易超时。

在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。

可以分为如下两步:

  • 统计每一个字符最后出现的位置
  • 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点

代码如下:

cs 复制代码
public class Solution {
    public IList<int> PartitionLabels(string s) {
        int[] location = new int[27];
        for (int i = 0; i < s.Length; i++)//记录每个元素出现的最远位置
        {
            location[s[i] - 'a'] = i;
        }
        List<int> res = new List<int>();
        int left = 0, right = 0;
        for (int i = 0; i < s.Length; i++)
        {
            right = Math.Max(right, location[s[i] - 'a']);//更新这个区间的最远位置
            if (i == right)//到达最远位置,划分
            {
                res.Add(right - left + 1);
                left = i + 1;
            }
        }
        return res;
    }
}

题目:合并区间

56. 合并区间 - 力扣(LeetCode)

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

题目分析:

和之前的题目差不多 ,排序之后比较

cs 复制代码
public class Solution {
    public int[][] Merge(int[][] intervals) {
        if (intervals.Length == 0)
            return intervals;
        Array.Sort(intervals,(a,b)=>{
            return a[0]-b[0];//从小到大排列
        });
        List<List<int>> res = new List<List<int>>();
        res.Add(intervals[0].ToList());
        for(int i=1;i<intervals.Length;i++){
            if(intervals[i][0]<=res[res.Count-1][1]){//和结果中最后一个区间重贴
                res[res.Count-1][1]=Math.Max(intervals[i][1],res[res.Count-1][1]);
            }
            else{
                res.Add(intervals[i].ToList());
            }
        }
        return res.Select(x => x.ToArray()).ToArray();
    }
}

题目:单调递增的数字

738. 单调递增的数字 - 力扣(LeetCode)

当且仅当每个相邻位数上的数字 xy 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增

题目分析:

暴力解放比较简单,对每一个数进行判断,当然肯定会超时。看看贪心的思路,例如:98,一旦出现strNumi - 1 > strNumi的情况(非单调递增),首先想让strNumi - 1--,然后strNumi给为9,这样这个整数就是89,即小于98的最大的单调递增整数。

此时是从前向后遍历还是从后向前遍历呢?

从前向后遍历的话,遇到strNumi - 1 > strNumi的情况,让strNumi - 1减一,但此时如果strNumi - 1减一了,可能又小于strNumi - 2

所以选择从后向前遍历。

cs 复制代码
public class Solution {
    public int MonotoneIncreasingDigits(int n) {
        char[] temp=n.ToString().ToCharArray();
        int flag=temp.Length;//记录那些位置需要改变
        for(int i=temp.Length-1;i>0;i--){
            if(temp[i-1]>temp[i]){
                flag=i;
                temp[i-1]--;
            }
        }
        for(int i=flag;i<temp.Length;i++){
            temp[i]='9';
        }
        return int.Parse(new string(temp));
    }
}

对于更详细的解析与其他语言的代码块,可以去代码随想录上查看。

代码随想录 (programmercarl.com)

已刷题目:86
相关推荐
Jerry29 分钟前
LeetCode 28. 找出字符串中第一个匹配项的下标
算法
Jerry2 小时前
LeetCode 459. 重复的子字符串
算法
海石5 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石5 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry7 小时前
LeetCode 151. 反转字符串中的单词
算法
a11177610 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜10 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry10 小时前
LeetCode 541. 反转字符串 II
算法
Jerry10 小时前
LeetCode 344. 反转字符串
算法
搞科研的小刘选手10 小时前
【香港大学主办&IEEE出版】第六届计算机视觉、应用与算法国际学术会议(CVAA 2026)
算法·计算机视觉·应用·学术会议