代码随想录算法训练营day36 || 453. 无重叠区间,763.划分字母区间,435. 合并区间

视频讲解:

贪心算法,依然是判断重叠区间 | LeetCode:435.无重叠区间_哔哩哔哩_bilibili

贪心算法,寻找最远的出现位置! LeetCode:763.划分字母区间_哔哩哔哩_bilibili 

贪心算法,合并区间有细节!LeetCode:56.合并区间_哔哩哔哩_bilibili

453. 无重叠区间

思路:本题的要求是 返回需要移除区间的最小数量,使剩余区间互不重叠,打好这道题可以使我们明确如何界定两个区间存在重叠,但是本题特殊的地方在于他不涉及重叠区间的更新操作。像前一天所做的射气球的问题,需要不断取重叠区间的交集部分用来表示射箭的区域,但是本题要求的是删去重叠区间中的一者使得区间彼此独立。

显然区间的比较是一个两两的比较关系,同时我们将区间按照下限或者上限递增排序之后,从左向右进行遍历,还可以保证当前遍历的位置无需考虑是否存在其左侧不会存在重叠的区间。这就是从左遍历下限最小的区间所产生的效果。我们以a代指当前遍历位置,b表示后一个位置,看似是比较a与b是否重合就可以满足局部题解,重叠集合之后更新的策略开始时我认为是保存跨越距离最小的集合就可以,但是这种情况推不出最优题解。应该是联合考虑b后一个的c位置,优化局部题解操作,使之通过不断积累,得到最优题解。我们看a,b,c三者的关系有这样几种,

  • a∩b,c∩b,c不∩a,  delete b;
  • a∩b,c∩b,c∩a,  delete b,c;
  • a∩b,c不∩b,  delete b;
  • a∩b,c不∩b,c∩a,  不可能;
  • a不∩b,c与b关系不需要明确,  指向b继续遍历;

回看这里所有的情况,其中最为关键的就是 a∩b,c∩b,c不∩a 这种,如果按照之前保存距离的方式,会导致a被删,c也被删,删的次数不满足贪心找最少的删除次数,这也就是导致我无法得到最优解的原因,所以集合的更新条件是,对于存在交集的集合,保留上限更小的集合,删除上限大的;上限越小与之后集合重合的概率越低,这就是我解题的贪心策略。

// 时间复杂度O(nlogn) 排序耗时更大
// 空间复杂度O(1)

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        // 递增排列排列
        Arrays.sort(intervals, (a,b)->{
            if(a[0] < b[0]) return -1;
            if(a[0] == b[0]) return 0;
                // if(a[1] < b[1]) return -1;
                // else if(a[1] == b[1])   return 0;
                // else return 1;
            return 1;
        });
        
        int ans = 0;
        int[] cur = intervals[0];
        for(int i=1; i<intervals.length; i++){
            // 不存在重叠,那么当前区间是可以留下来的,cur自动移动下个区间
            if(intervals[i][0] >= cur[1])
                cur = intervals[i];
            else{
                if(cur[1] > intervals[i][1])
                    cur = intervals[i];
                ans++;
            }
        }
        // 优化一下
        // int ans = 0;
        // int cur = intervals[0][1];
        // for(int i=1; i<intervals.length; i++){
        //     // 不存在重叠,那么当前区间是可以留下来的,cur自动移动下个区间
        //     if(intervals[i][0] >= cur)
        //         cur = intervals[i][1];
        //     else{
        //         if(cur > intervals[i][1])
        //             cur = intervals[i][1];
        //         ans++;
        //     }
        // }
        return ans;
    }
}

优化后时间效率与原来的解法差不多。

763.划分字母区间

思路:本题可以理解为获取到每个字母出现的范围区间,然后输出不存在重叠的区间的区间长度。所以在不断遍历字符串s的过程中记录各个字母出现的最大右位置作为全局上限,如果当前i与全局全局上限相等,那说明i之前的元素可以被分割在一个片段之内。

全局上限储存了其中出现于最为右端的字母的索引下标,其他的字母的最后一次出现位置都小于他,因此可以将这个片段分割出来,然后继续访问字符串剩余元素开始继续遍历。

// 时间复杂度O(n^2),因为lastIndexOf时间开销为O(n)
// 空间复杂度O(n)

class Solution {
    public List<Integer> partitionLabels(String s) {
        
        // 重叠区间的关键求解思路就是不断遍历新的范围,不会更新最大右上限,如果出现下限都比当前最大上限来的大则说明不重叠,或者类似出现了可以分割的便捷位置。
        int splitIndex = -1;
        int preSplit = 0;
        List<Integer> ans = new ArrayList<>();

        for (int i=0; i<s.length(); i++) {
            char ch = s.charAt(i);
            // 计算每个字母的最长有边界
            if(s.lastIndexOf(ch) > splitIndex)
                splitIndex = s.lastIndexOf(ch);

            if(i == splitIndex){
                ans.add(splitIndex - preSplit + 1);
                preSplit = splitIndex+1;    // 怕pre会地址越界,那么此时split是最后一个元素,那么i也是,那么马上for循环就结束了
            }

        }

        return ans;
    }
}

435. 合并区间

思路:在射箭以及无重叠区间所形成的先排序再操作重叠区间的思路之下,本题于我个人而言是今天三题中最简单的一题,区间重合时的更新策略也非常的清晰,就是形成并集,对于不重合的区间,就将指针移动到区间位于数轴更为右侧的那个区间即可。

// 时间复杂度O(nlogn)
// 空间复杂度 小于等于O(n)
class Solution {
    public int[][] merge(int[][] intervals) {
        // 优先排序,防止出现一个非常偏远的范围出现在密集的范围之中引发问题
        Arrays.sort(intervals, (a,b)->{
            return a[0]-b[0];
        });

        List<int[]> list = new ArrayList<>();
        int[] cur = intervals[0];
        for(int i=1; i<intervals.length; i++){
            // 存在重叠
            if(intervals[i][0] <= cur[1]){
                cur[0] = Math.min(intervals[i][0], cur[0]);
                cur[1] = Math.max(intervals[i][1], cur[1]);
            }else{
                list.add(cur);
                cur = intervals[i];
            }
        }
        list.add(cur);
        int[][] res = new int[list.size()][2];
        for(int i=0; i<list.size(); i++)
            res[i] = list.get(i);
        return res;
    }
}

一点小总结:

针对重叠区间类型的题目,自己形成的惯用解法就是先排序,然后按照是否重合的操作对每个元素进行整合,可附带构建额外的空间存储已经独立的空间。另外如果不断重叠空间有合并的需求,那么在进行遍历时,只需要不断更新所访问到的最大上限,利用上限值与新的位置的下限值进行比较,判断是否存在重叠,然后更新最大上限,将区间进一步向后推,也符合排序之后从左向右进行访问的过程。

  • 需要合并区间,则排序+当前指针指向元素更新+额外空间存储独立可行集合;
  • 不需要合并区间,则排序+保存最大上限;
  • 不需要合并空间,并且是类似于字符串分割这样的自动形成的有序区间,则直接更新并保存最大上限求解即可。
  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值