贪心算法练习题(7/2)

1无重叠区间

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

示例 1:

复制代码
输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:

复制代码
输入: intervals = [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

复制代码
输入: intervals = [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。

提示:

  • 1 <= intervals.length <= 105
  • intervals[i].length == 2
  • -5 * 104 <= starti < endi <= 5 * 104

思路:

  1. 按起始位置排序

    • 首先,根据区间的起始位置对它们进行排序。这种排序可以有效地通过确保按起始位置顺序处理区间来检查重叠。
  2. 贪心策略

    • 初始化 count 为 0,用于记录需要移除的重叠区间数量。
    • 遍历排序后的区间,从第二个区间开始。
    • 对于每个区间,检查其起始位置是否小于前一个区间的结束位置。这表示存在重叠。
    • 如果存在重叠:
      • 增加 count
      • 更新当前区间的结束位置为其当前结束位置与前一个区间结束位置的最小值。这确保当前区间调整以消除与前一个区间的重叠。
  3. 结果

    • 循环结束时,count 的值即为最大可保留的不重叠区间数量。

代码:

cpp 复制代码
class Solution {
public:
    // 自定义比较函数,按照区间起始位置从小到大排序
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        return a[0] < b[0];
    }
    // 求解无重叠区间的最大数量
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        // 若区间数量为0,返回0
        if (intervals.size() == 0) return 0;
        // 按照区间起始位置排序
        sort(intervals.begin(), intervals.end(), cmp);
        int count = 0; // 记录重叠区间数量
        // 遍历区间数组
        for (int i = 1; i < intervals.size(); i++) {
            // 若当前区间起始位置小于前一个区间的结束位置,说明存在重叠
            if (intervals[i][0] < intervals[i - 1][1]) {
                count++; // 计数加1
                // 更新当前区间的结束位置为前一个区间结束位置与当前区间结束位置的最小值
                intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]);
            }
        }
        return count; // 返回重叠区间数量
    }
};

2划分字母区间

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

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

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

示例 1:

复制代码
输入:s = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。 

示例 2:

复制代码
输入:s = "eccbbbbdec"
输出:[10]

提示:

  • 1 <= s.length <= 500
  • s 仅由小写英文字母组成

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

  1. 统计字符出现的最后位置

    • 使用一个数组 hash,其中 hash[i] 记录字符 'a' + i 最后一次出现的位置。
  2. 贪心策略

    • 使用两个指针 leftright 来表示当前区间的起始和结束位置。
    • 遍历字符串 S,对于每个字符,更新 right 为当前字符的最后出现位置。
    • 如果当前遍历的位置 i 等于 right,则表示当前区间可以划分,将当前区间的长度加入结果中,并更新 left 到下一个字符的位置。
  3. 结果

    • 遍历完成后,得到所有划分区间的长度,即为最终结果。

代码:

cpp 复制代码
class Solution {
public:
    vector<int> partitionLabels(string S) {
        int hash[26] = {0}; // 存储每个字符的最后出现位置
        for (int i = 0; i < S.size(); i++) {
            hash[S[i] - 'a'] = i;
        }
        
        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;
    }
};
相关推荐
孑渡2 分钟前
【LeetCode】每日一题:跳跃游戏
python·算法·leetcode·游戏·职场和发展
liulanba4 分钟前
leetcode--二叉树中的最长交错路径
linux·算法·leetcode
Puppet__9 分钟前
【康复学习--LeetCode每日一题】3115. 质数的最大距离
学习·算法·leetcode
dc爱傲雪和技术14 分钟前
卡尔曼滤波Q和R怎么调
python·算法·r语言
Neituijunsir30 分钟前
2024.06.28 校招 实习 内推 面经
c++·python·算法·面试·自动驾驶·汽车·求职招聘
xw-pp39 分钟前
回溯法的小结与概述
java·数据结构·c++·python·算法·递归
每天努力进步!1 小时前
LeetCode热题100刷题6:160. 相交链表、206. 反转链表、234. 回文链表、141. 环形链表、142. 环形链表 II
c++·算法·leetcode·链表
weixin_432702761 小时前
统计信号处理基础 习题解答11-1
算法·信号处理
爱编程的Tom1 小时前
Map && Set(Java篇详解)
java·开发语言·数据结构·学习·算法