Day29 力扣贪心 : 860.柠檬水找零| 406.根据身高重建队列 |452. 用最少数量的箭引爆气球

今天的三道题目,都算是 重叠区间 问题,大家可以好好感受一下。 都属于那种看起来好复杂,但一看贪心解法,惊呼:这么巧妙!
还是属于那种,做过了也就会了,没做过就很难想出来。 不过大家把如下三题做了之后, 重叠区间 基本上差不多了

435. 无重叠区间

https://programmercarl.com/0435.%E6%97%A0%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4.html

第一印象

和上一道题好像,但我不知道[1,2] [1,3]的排序结果是什么

看完题解的思路

也没有强调我的上面说的疑问。

首先排序,然后去找重叠区间的个数,其实和射箭是一样的,每一只箭就是一组重叠区间。只不过射箭是重叠的时候什么都不干,现在是重叠的时候计数。

我做重叠区间问题常有一个疑问

ab重叠,bc重叠,ac不重叠的情况。

其实这种就算ab重叠,ab整体和c不重叠。

射箭的话,一支箭不能射三个。

重叠区间的话,只要删除了b就没有重叠了,所以只是算一个。不然删除a,bc依然重叠,题目要求删最少。

实现中的困难

实现没困难

感悟

感受重叠区间问题吧

代码

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        int result = 0;
        //排序  
        Arrays.sort(intervals, (a,b)-> {
            return Integer.compare(a[0],b[0]);
        });

        for (int i = 1; i < intervals.length; i++) {
            //我的左边界如果小于上一个的右边界
            if (intervals[i][0] < intervals[i - 1][1]) {
                result++;
                intervals[i][1] = Math.min(intervals[i][1], intervals[i - 1][1]);
            }
        }
        return result;
    }
}

763.划分字母区间

https://programmercarl.com/0763.%E5%88%92%E5%88%86%E5%AD%97%E6%AF%8D%E5%8C%BA%E9%97%B4.html

第一印象

每个字母都有开始出现和结束出现的位置。这个就相当于是区间,我们给这个区间排序。

如果两个区间有重叠,说明他们要分在一段,比如a [1,9] b [5,11],那么如果划分1~9,就不能保证b,因为5~9里有b,反之亦然。所以要划分1~11.

所以重叠区间就是一个片段,重叠之后视为整体,取大的那个右边界作为片段的右边界,再继续去找重叠……

我觉得思路是对的,但是就是怎么实现的问题了。

做出来了!!!!!!!!!!!!!!!!

看完题解的思路

写完实现困难回来看题解

题解有个非重叠区间的解法,不管了

实现中的困难

思路就是

获得每个字母的左右区间 -->
重叠的话就更新左右区间 -->
不重叠的话就记录上一个区间大小 (因为是上一个区间和我不重叠,那就是上一个区间已经结束了,就这么大) -->
最后记录最后一个区间的大小

我选择了数组作为哈希表,因为只有26个字母。那就涉及到一个问题,字符串不一定有26个不同字母啊,那排序的时候就一堆[0,0] 出来恶心人。

但也不能跳过所有[0,0] 去找第一个[0, k] 的,因为字符串可能是 abbbbb,a 就是[0,0] ,所以我初始化所有字母的下标为 MAX_VALUE。

获得每个字母区间的时候,遇到下标 MAX_VALUE 就说明第一次遇到这个字母,就要同时更新左右边界 (我在自己做的时候只更新了左边界,根据没过的用例改过来了)。如果遇到下标不是 MAX_VALUE 就说明第二次遇到这个字母,就只更新右边界。

再排序,我依然不知道Lambda表达式哈哈哈哈

再判断重叠的逻辑,不难这里。因为升序排列了,那些没出现的字母因为是MAX_VALUE,所以肯定都在最后面,如果遇到一个MAX_VALUE的,就break就行了。

  //如果没有这个字母就break了,因为升序排列呀,没有的都是在最后面,只要有一个是这样的,就说明后面都是没有的。
if (hash[i][0] == Integer.MAX_VALUE) {
    break;
}

同时也要记录一下有效字母到哪,因为要收集最后那个片段的长度,需要知道hash里有效字母到哪个位置。

手机最后片段的长度,就ok了。

自己做出来了舒服死了。

感悟

牛逼啊我!

代码

class Solution {
    public List<Integer> partitionLabels(String s) {
        int[][] hash = new int[26][26];
        List<Integer> result = new ArrayList<>();
        //初始化,每个字母的起始位置都是MAX_INTEGER
        for (int i = 0; i < hash.length; i++) {
            hash[i][0] = Integer.MAX_VALUE;
        }

        //获得每个字母的区间
        for (int i = 0; i < s.length(); i++) {
            char letter = s.charAt(i);
            int index = letter - 'a';
            //如果这个字母第一次遇到,更新左右边界
            if (hash[index][0] == Integer.MAX_VALUE) {
                hash[index][0] = i;
                hash[index][1] = i;
            } else {
                //第二次遇到的话就只更新右边界
                hash[index][1] = i;
            }
        }
        //排序
        Arrays.sort(hash, (a, b) -> Integer.compare(a[0], b[0]));
        int lastIndex = 0;
        for (int i = 1; i < hash.length; i++) {
            //如果没有这个字母就break了,因为升序排列呀,没有的都是在最后面,只要有一个是这样的,就说明后面都是没有的。
            if (hash[i][0] == Integer.MAX_VALUE) {
                break;
            }
            //我的左边界比上一个的右边界大,我俩不重复
            if (hash[i][0] > hash[i - 1][1]) {
                int length = hash[i - 1][1] - hash[i - 1][0] + 1;
                result.add(length);
                //记录这个hash里有效字母到哪
                lastIndex = i;
            } else {
                //我的左边界比上一个的右边界小,我俩重复了
                //我俩的右边界得是最大的那个边界
                hash[i][1] = Math.max(hash[i][1], hash[i - 1][1]);
                //我俩的左边界得是最小的边界, 为了记录这个片段的起始位置
                hash[i][0] = Math.min(hash[i][0], hash[i - 1][0]);
                //记录这个hash里有效字母到哪
                lastIndex = i;
            }
        }
        //收获最后一个片段的长度
        int lastLength = hash[lastIndex][1] - hash[lastIndex][0] + 1;
        result.add(lastLength);

        return result;

    } }

56. 合并区间

本题相对来说就比较难了。

https://programmercarl.com/0056.%E5%90%88%E5%B9%B6%E5%8C%BA%E9%97%B4.html

第一印象

我和上一个重叠的话,同时更新我的左右边界。这个感觉还没我上一道做的难呢,试试吧。

答案是作对了,但是result 最后莫名其妙多出来一个[0,0]

我看看怎么回事

因为我result的声明是

int[][] result = new int[intervals.length][2];

初始化的时候就会多东西

应该用LinkedList

看完题解的思路

和我一样,无敌啦~!!!!!!!

实现中的困难

对LinkedList 泛型是数组的时候完全不会,还有LinkedList 变成二维数组的语法。。。。。

晕啦

//list里都是数组
LinkedList<int[]> result = new LinkedList<>();
//往list里加元素,每个元素都是数组,直接把左右边界初始化进去
result.add( new int[] {a, b} );
//list转成数组,第一个参数要是list的大小,这个我看不懂
result.toArray( new int [ result.size() ] [] );

上面的第三个的参数我看不懂意义。是因为result里面的东西都是第二个参数的,所以只需要指明变成二维数组的话,大小是多少就行了吗??

感悟

主要是java语言基础不过关,重复区间已经无敌

代码

class Solution {
    public int[][] merge(int[][] intervals) {
         LinkedList<int[]> result = new LinkedList<>();
        Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));


        for (int i = 1; i < intervals.length; i++) {
            //我的左边界小于等于上一个的右边界,我俩重叠, 我俩合并
            if (intervals[i][0] <= intervals[i - 1][1]) {
                //重叠就更新我的左右边界,左边的选最小的,右边的选最大的
                intervals[i][0] = intervals[i - 1][0];
                intervals[i][1] = Math.max(intervals[i][1], intervals[i - 1][1]);
            } else {
                //我和上一个不重叠的话
                //记录上一个的左右边界,就是一个区间的答案。
                result.add(new int[] {intervals[i - 1][0], intervals[i - 1][1]});
            }
        }

        //最后收集最后一个区间的答案
         result.add(new int[] {intervals[intervals.length - 1][0], intervals[intervals.length - 1][1]});

        return result.toArray(new int[result.size()][]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值