首页 > 编程语言 >代码随想录算法训练营Day36 贪心算法

代码随想录算法训练营Day36 贪心算法

时间:2023-03-08 14:57:38浏览次数:55  
标签:vector 边界 Day36 随想录 算法 intervals result 区间 重叠

代码随想录算法训练营

代码随想录算法训练营Day36 贪心算法| 435. 无重叠区间 763.划分字母区间 56. 合并区间

435. 无重叠区间

题目链接:435. 无重叠区间
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意: 可以认为区间的终点总是大于它的起点。 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
示例 1:

  • 输入: [ [1,2], [2,3], [3,4], [1,3] ]
  • 输出: 1
  • 解释: 移除 [1,3] 后,剩下的区间没有重叠。

总体思路

看到本题后肯定要排序,但是究竟是按照右边界排序,还是按照左边界排序呢?
其实都可以。主要就是为了让区间尽可能的重叠。
按照右边界排序,从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了
此时问题就是要求非交叉区间的最大个数。
这里记录非交叉区间的个数还是有技巧的,如图

区间,1,2,3,4,5,6都按照右边界排好序。
当确定区间 1 和 区间2 重叠后,如何确定是否与 区间3 也重贴呢?
就是取 区间1 和 区间2 右边界的最小值,因为这个最小值之前的部分一定是 区间1 和区间2 的重合部分,如果这个最小值也触达到区间3,那么说明 区间 1,2,3都是重合的。
接下来就是找大于区间1结束位置的区间,是从区间4开始。那有同学问了为什么不从区间5开始?别忘了已经是按照右边界排序的了
区间4结束之后,再找到区间6,所以一共记录非交叉区间的个数是三个。
总共区间个数为6,减去非交叉区间的个数3。移除区间的最小数量就是3。

class Solution {
public:
    // 按照区间右边界排序
    static bool cmp (const vector<int>& a, const vector<int>& b) {
    return a[1]<b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
	    if (intervals.size() == 0) return 0;
        sort(intervals.begin(), intervals.end(), cmp);
        int count = 1; // 记录非交叉区间的个数
        int end = intervals[0][1]; // 记录区间分割点
        for (int i = 1; i < intervals.size(); i++) {
            if (end <= intervals[i][0]) {
                end = intervals[i][1];
                count++;
            }
        }
        return intervals.size()-count;
    }
}

763.划分字母区间

题目链接:763.划分字母区间
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
示例:

  • 输入:S = "ababcbacadefegdehijhklij"
  • 输出:[9,7,8] 解释: 划分结果为 "ababcbaca", "defegde", "hijhklij"。 每个字母最多出现在一个片段中。 像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
    提示:
  • S的长度在[1, 500]之间。
  • S只包含小写字母 'a' 到 'z'

总体思路

题目要求同一字母最多出现在一个片段中,那么如何把同一个字母的都圈在同一个区间里呢?
如果没有接触过这种题目的话,还挺有难度的。
在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。
可以分为如下两步:

  • 统计每一个字符最后出现的位置
  • 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
    如图:
class Solution {
public:
    vector<int> partitionLabels(string S) {
        int hash[27] = {0}; // i为字符,hash[i]为字符出现的最后位置
        for (int i = 0; i < S.size(); i++) { // 统计每一个字符最后出现的位置
            hash[S[i] - 'a'] = i;//S[i]-'a'指每个字母的位置
        }
        vector<int> result;
        int left = 0;
        int right = 0;
        for (int i = 0; i < S.size(); i++) {
            right = max(right, hash[S[i] - 'a']); // 找到字符出现的最远边界
            if (i == right) {
                result.push_back(right - left + 1);
                left = i + 1;
            }
        }
        return result;
    }
};

56. 合并区间

题目链接:56. 合并区间
给出一个区间的集合,请合并所有重叠的区间。
示例 1:

  • 输入: intervals = [[1,3],[2,6],[8,10],[15,18]]
  • 输出: [[1,6],[8,10],[15,18]]
  • 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

总体思路

本题的本质其实还是判断重叠区间问题。
这几道题都是判断区间重叠,区别就是判断区间重叠后的逻辑,本题是判断区间重贴后要进行区间合并。
所以一样的套路,先排序,让所有的相邻区间尽可能的重叠在一起,按左边界,或者右边界排序都可以,处理逻辑稍有不同。
按照左边界从小到大排序之后,如果 intervals[i][0] <= intervals[i - 1][1] 即intervals[i]的左边界 <= intervals[i - 1]的右边界,则一定有重叠。(本题相邻区间也算重贴,所以是<=)

其实就是用合并区间后左边界和右边界,作为一个新的区间,加入到result数组里就可以了。如果没有合并就把原区间加入到result数组。

class Solution {
public:
   vector<vector<int>> merge(vector<vector<int>>& intervals) {
       vector<vector<int>> result;
       if (intervals.size() == 0) return result; // 区间集合为空直接返回
       // 排序的参数使用了lambda表达式
       sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b){return a[0] < b[0];});

       // 第一个区间就可以放进结果集里,后面如果重叠,在result上直接合并
       result.push_back(intervals[0]); 

       for (int i = 1; i < intervals.size(); i++) {
           if (result.back()[1] >= intervals[i][0]) { // 发现重叠区间
               // 合并区间,只更新右边界就好,因为result.back()的左边界一定是最小值,因为我们按照左边界排序的
               result.back()[1] = max(result.back()[1], intervals[i][1]); 
           } else {
               result.push_back(intervals[i]); // 区间不重叠 
           }
       }
       return result;
   }
};

标签:vector,边界,Day36,随想录,算法,intervals,result,区间,重叠
From: https://www.cnblogs.com/bailichangchuan/p/17191976.html

相关文章

  • 机器人运动|浅谈Time Elastic Band算法
    前言在自主移动机器人路径规划的学习与开发过程中,我接触到TimeElasticBand算法,并将该算法应用于实际机器人,用于机器人的局部路径规划。在此期间,我也阅读了部分论文、官方......
  • 【算法设计-分治】递归与尾递归
    目录1.阶乘尾递归:递归的进一步优化2.斐波那契数列3.最大公约数(GCD)4.上楼梯5.汉诺塔(1)输出移动过程输出移动步数5.汉诺塔(2)输出移动过程输出移动步数6.杨辉三角形7.完......
  • Paxos算法理解与java实现
    Paxos在分布式环境下应用非常广泛,是一致性算法里面优越的代表。Google的粗粒度锁服务Chubby的设计开发者Burrows曾经说过:所有一致性协议本质上要么是Paxos要么是其变体。......
  • KMP算法
    KMP算法是字符串匹配算法,就是从指定字符串里找到匹配串匹配的位置字符串匹配无非是一个个去匹配单个字符,按照通常的思路,我们只需要从头开始一个个往下比就是,但是这样的效......
  • 【LeetCode回溯算法#02】组合总和III
    组合总和III力扣题目链接(opensnewwindow)找出所有相加之和为n的k个数的组合。组合中只允许含有1-9的正整数,并且每种组合中不存在重复的数字。说明:所有数字......
  • Raft算法分析
      Raft是一种更为简单方便易于理解的分布式算法,主要解决了分布式中的一致性问题。相比传统的Paxos算法,Raft将大量的计算问题分解成为了一些简单的相对独立的子问题,......
  • 代码随想录算法训练营第七天 | 454. 四数相加 II、383. 赎金信、
    454.四数相加IIclassSolution{public:intfourSumCount(vector<int>&nums1,vector<int>&nums2,vector<int>&nums3,vector<int>&nums4){unord......
  • 【LeetCode回溯算法#01】图解组合问题
    组合问题力扣题目链接(opensnewwindow)给定两个整数n和k,返回范围[1,n]中所有可能的k个数的组合。示例:输入:n=4,k=2输出:[[2,4],[3,4],[2,3],[1......
  • 缓存算法介绍
    缓存算法(页面置换算法)之LRU算法LRU进阶之LRU-K和2Q缓存淘汰算法(LFU、LRU、ARC、FIFO、2Q)分析LRU(LeastRecentlyUsed)算法思想每次内存溢出时,把最长时间未被访......
  • 一道某度的笔试算法题
    题目:给定一个长度为n,由非零整数成的数组,求连续子数组乘积为负数的个数example:55-33-1178ChatGPT的答案,基本正确,有个地方多+1了n=int(input())arr=list(......