算法训练1-day26-贪心

16 阅读2分钟
  1. 452. 用最少数量的箭引爆气球

AC代码:

class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        sort(points.begin(), points.end(),
             [](vector<int> a, vector<int> b) { return a[0] < b[0]; });
        int left = points[0][0];
        int right = points[0][1];
        int ans = 1;
        for (int i = 1; i < points.size(); ++i) {
            vector<int> point = points[i];
            // 新的左边界要比原来的右边界还要大
            if (point[0] > right) {
                ans++;
                left = point[0];
                right = point[1];
            }
            // 更大的左边界
            if (point[0] > left) {
                left = point[0];
            }
            // 更小的右边界
            if (point[1] < right) {
                right = point[1];
            }
        }

        return ans;
    }
};
  1. 435. 无重叠区间 无重叠区间问题的贪心算法核心思想是:优先选择结束时间早的区间,这样可以为后面的区间留下更多的空间。 算法步骤:
    1. 按结束时间排序:将所有区间按照结束时间升序排列
    2. 贪心选择:遍历排序后的区间,每次选择结束时间最早且不与已选区间重叠的区间
    3. 计数:统计需要移除的区间数量 AC代码:
class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(),
             [](vector<int> a, vector<int> b) { return a[1] < b[1]; });
        int ans = 0;
        int right = intervals[0][1];
        for (int i = 1; i < intervals.size(); ++i) {
            if (intervals[i][0] < right) {
                ans++;
                continue;
            }
            right = intervals[i][1];
        }

        return ans;
    }
};
  1. 763. 划分字母区间

代码如下:

class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<vector<int>> positions;
        unordered_map<char, int> s_map;
        int i = 0;
        // 获取每个字母出现的左右边界
        for (int j = 0; j < s.size(); ++j) {
            if (s_map.find(s[j]) == s_map.end()) {
                s_map.emplace(s[j], i);
                positions.push_back(vector<int>{j, j});
                i++;
            } else {
                positions[s_map[s[j]]][1] = j;
            }
        }

        int left = positions[0][0];
        int right = positions[0][1];
        vector<int> ans;
        for (int j = 1; j < positions.size(); ++j) {
            // 如果当前右边界小于当前查看位置的左边界,则可以在此划分
            if (positions[j][0] > right) {
                ans.push_back(right - left + 1);
                left = positions[j][0];
                right = positions[j][1];
            } else {
                // 否则,当前位置的字符也要归为同一划分中
                // 此时,如果当前位置的右边界大于right,则右边界要向右延伸
                if (positions[j][1] > right) {
                    right = positions[j][1];
                }
            }
        }
        ans.push_back(right - left + 1);

        return ans;
    }
};

// 可以只记录字母最远的出现位置,然后从前往后遍历S
// 维护right为前面所有字符的最后出现的位置的最大值
// 当i等于right时,就表示前面出现的字母最远就到这个边界了,这里就是分割点
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;
        }
        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;
    }
};