算法训练1-day27-贪心

24 阅读4分钟
  1. 56. 合并区间

AC代码:

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(),
             [](vector<int> a, vector<int> b) { return a[0] < b[0]; });
        int left = intervals[0][0];
        int right = intervals[0][1];
        vector<vector<int>> ans;
        for (int i = 1; i < intervals.size(); ++i) {
            if (right < intervals[i][0]) {
                ans.push_back(vector<int>{left, right});
                left = intervals[i][0];
                right = intervals[i][1];
            } else {
	            // 注意[[1,4],[2,3]]的情况
	            // 只有在right小于当前查看区间的右边界时才更新right
                if (right < intervals[i][1]) {
                    right = intervals[i][1];
                }
            }
        }
        // 记得把最后一个区间补上
        // 对于最后一个合并区间,不会有某个小区间的左边界的值大于right了
        ans.push_back(vector<int>{left, right});

        return ans;
    }
};

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;
    }
};
  1. 738. 单调递增的数字

重点在于找到从左往右第一个递减的位置,要求尽可能大且要小于当前数,所以,例如3321,3到2是递减的,此时最接近32的就是29,又因为要递增,所以后面的所有数也应该是变为9,;另外,此时3变成2后,前面又不满足递增的要求了,所以我们要继续往前遍历,因此我们从后往前遍历,记录最后的递减位置,将这个位置之后的所有数都改为9

AC代码:

class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string s = to_string(n);
        int pos = s.size();
        for (int i = s.size() - 1; i > 0; --i) {
            if (s[i - 1] > s[i]) {
                pos = i;
                s[i - 1]--;
            }
        }
        for (int i = pos; i < s.size(); ++i) {
            s[i] = '9';
        }
        return stoi(s);
    }
};
  1. 968. 监控二叉树

代码如下:


class Solution {
	// 四种状态,只有子节点返回CameraFatherFather和CameraChild时才将当前节点设置为相机
	// 换句话说,就是希望一个相近尽可能的监视更多的节点
    enum State { CameraFatherFather, CameraFather, Camera, CameraChild };

public:
    int minCameraCover(TreeNode* root) {
        int ans = 0;
        State state = GetMinCameraCover(root, ans);
        if (state == State::CameraChild || state == State::CameraFatherFather) {
            ans++;
        }
        return ans;
    }
    State GetMinCameraCover(TreeNode* root, int& ans) {
	    // 空节点视为被监视
        if (root == nullptr)
            return State::CameraFather;
        // 叶子节点必须要有一个节点去监视
        if (root->left == nullptr && root->right == nullptr)
            return State::CameraChild;

        State leftState = GetMinCameraCover(root->left, ans);
        State rightState = GetMinCameraCover(root->right, ans);
        // 如果左右节点有一个是叶子节点,那么父节点必须是相机
        if (leftState == State::CameraChild ||
            rightState == State::CameraChild) {
            ans++;
            return State::Camera;
        }
        // 如果子节点是相机的父节点,那么代表子节点是没有被监视的
        // 为了用更少的相机监视更多的节点,我们不用子节点作相机,而是当前这个父节点
        if (leftState == State::CameraFatherFather ||
            rightState == State::CameraFatherFather) {
            ans++;
            return State::Camera;
        }
        // 子节点有一个是相机,代表当前节点是被监视的
        if (leftState == State::Camera || rightState == State::Camera) {
            return State::CameraFather;
        }

        return State::CameraFatherFather;
    }
};

// 版本一
class Solution {
private:
    int result;
    int traversal(TreeNode* cur) {

        // 空节点,该节点有覆盖
        if (cur == NULL) return 2;

        int left = traversal(cur->left);    // 左
        int right = traversal(cur->right);  // 右

        // 情况1
        // 左右节点都有覆盖
        if (left == 2 && right == 2) return 0;

        // 情况2
        // left == 0 && right == 0 左右节点无覆盖
        // left == 1 && right == 0 左节点有摄像头,右节点无覆盖
        // left == 0 && right == 1 左节点有无覆盖,右节点摄像头
        // left == 0 && right == 2 左节点无覆盖,右节点覆盖
        // left == 2 && right == 0 左节点覆盖,右节点无覆盖
        if (left == 0 || right == 0) {
            result++;
            return 1;
        }

        // 情况3
        // left == 1 && right == 2 左节点有摄像头,右节点有覆盖
        // left == 2 && right == 1 左节点有覆盖,右节点有摄像头
        // left == 1 && right == 1 左右节点都有摄像头
        // 其他情况前段代码均已覆盖
        if (left == 1 || right == 1) return 2;

        // 以上代码我没有使用else,主要是为了把各个分支条件展现出来,这样代码有助于读者理解
        // 这个 return -1 逻辑不会走到这里。
        return -1;
    }

public:
    int minCameraCover(TreeNode* root) {
        result = 0;
        // 情况4
        if (traversal(root) == 0) { // root 无覆盖
            result++;
        }
        return result;
    }
};