合并区间
和之前的思路类似,先创建一个ans二维数组,创建start和end来指明添加进入ans数组的区间下标,先对数组按照首元素排序从小到大排序后,根据当前元素是否小于下一个元素的第一个元素来决定将这个对ans数组进行增加、移动下标或改变end。具体代码如下。
class Solution {
public:
// 定义一个比较函数,用于sort函数,按照区间的起始点进行排序
static bool cmp(vector<int>&a, vector<int>&b){
return a[0]<b[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
// 使用sort函数和自定义的比较函数cmp对区间进行排序
sort(intervals.begin(),intervals.end(),cmp);
// 定义一个二维向量用于存放合并后的区间
vector<vector<int>>ans;
// 如果输入的区间为空,直接返回空的ans
if(intervals.size() == 0){
return ans;
}
// 如果只有一个区间,直接将该区间放入ans中
if(intervals.size() == 1){
ans.push_back(vector<int>{intervals[0][0],intervals[0][1]});
return ans;
}
// 初始化起始和结束点为第一个区间的起始和结束点
int begin = intervals[0][0];
int end = intervals[0][1];
for(int i = 0 ; i < intervals.size()-1; i++){
// 如果当前区间的起始点大于上一个区间的结束点,说明没有重叠
if(intervals[i+1][0] > end){
// 将上一个区间加入ans中
ans.push_back(vector<int>{begin,end});
// 更新区间的起始和结束点为当前区间的起始和结束点
begin = intervals[i+1][0];
end = intervals[i+1][1];
}
else
// 如果有重叠,更新结束点为当前区间和上一个区间结束点的较大值
end = max(intervals[i+1][1],end);
// 如果是最后一个区间,将其加入ans中
if(i == intervals.size()-2){
ans.push_back(vector<int>{begin,end});
}
}// 返回合并后的区间
return ans;
}
};
算法的时间复杂度为O(nlogn),空间复杂度为O(n)。
代码随想录中代码
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;
}
};
- 时间复杂度: O(nlogn)
- 空间复杂度: O(logn),排序需要的空间开销
单调递增的数字
贪心算法,98的最小单调递增数字为89,第一位不符合单调递增的情况,将该值--,并将后面的数字都变为9,使用字符串可以方便对数字进行处理,所以我们先将数字转换为字符串,然后寻找到第一个不符合单调递增情况的数字,之后对该数字以前的数字全部-1,保证其前几位在符合单调递增的情况下最大,最后把后面的数字全部变为9,即实现了最大的单调递增数字。
class Solution {
public:
int monotoneIncreasingDigits(int n) {
// 将整数n转换为字符串s,以便逐位处理
string s = to_string(n);
int i = 1;
// 从左到右遍历字符串,直到遇到一个位置i,使得s[i-1] > s[i]
// 找到需要调整的位置
while(i < s.size() && s[i-1]<=s[i]){
i++;
}
// 如果i没有遍历到字符串的末尾,说明我们找到了需要调整的位置
if(i < s.size()){
// 从位置i开始,向前遍历,直到s[i-1]不再大于s[i]
// 将每个大于其后继的数字减1,这样可以保证数字尽可能大且单调递增
while(i > 0 && s[i-1] > s[i]){
s[i - 1] -= 1;
i-- ;
}
// 将位置i之后的所有数字都置为'9',这样可以保证这些位置的数字是最大的
for(int j = i + 1; j < s.size(); j++){
s[j] = '9';
}
}
// 将处理后的字符串s转换回整数并返回
return stoi(s);
}
};
时间复杂度为O(n),空间复杂度为O(n)。
监控二叉树
我开始的想法是用一个层序遍历,然后将偶数层的节点相加得到监控的数目,有些问题,后面再改改。(能力不够,先跳过吧)
class Solution {
public:
int minCameraCover(TreeNode* root) {
queue<TreeNode*> queue;
if(root!=nullptr){
queue.push(root);
}
int count = 0;
int flag = 0;
TreeNode*cur = root;
vector<int>ans;
while(!queue.empty()){
int size = queue.size();
ans.push_back(size);
for(int i = 0; i < size; i++){
cur = queue.front();
queue.pop();
if(cur->left){
queue.push(cur->left);
}
if(cur->right){
queue.push(cur->right);
}
}
}
if(ans.size() <= 2){
return 1;
}
for(int i = 1 ; i < ans.size(); i = i + 2){
count += ans[i];
}
return count;
}
};
代码随想录思路
代码随想录 (programmercarl.com)一个监控摄像头最多可以监控父节点、自己和两个子节点,累计三层,若想要最小化摄像头的数目,最优的方式必定要利用好这个特点。叶节点远多于根节点,考虑在叶节点处节约摄像头的数目,选择叶节点的父节点作为摄像头的放置位置。遍历到根节点再放置一个摄像头,所以使用后序遍历。
贪心算法,二叉树与贪心的结合,有点难...... LeetCode:968.监督二叉树_哔哩哔哩_bilibili
思路不好想啊。。。。
二叉树中的每个节点有3个状态。0.无覆盖、1.有摄像头、2.有覆盖
后序遍历,从叶节点往上遍历,总共有四种情况。
1.左右孩子都有覆盖,返回0,此处为无覆盖
2.左右节点存在无覆盖情况,必须放入一个摄像头
3.左右至少存在一个摄像头,当前位置状态为有覆盖
4.根节点状态为无覆盖,放置一个摄像头。
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;
}
};
算法时间复杂度为O(n),空间复杂度O(n)。