56 合并区间
题目链接
思路
和前面重叠区间的思路相同。这里需要用到vector::back()函数,来获取结果集当前最后区间的右边界。
文章详解
cpp
class Solution {
private:
static bool cmp(vector<int> a,vector<int>b)
{
return a[0] < b[0];
}
public:
vector<vector<int>> result;
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.size() == 0)
{
return result;
}
sort(intervals.begin(),intervals.end(),cmp);
result.push_back(intervals[0]);
for(int i = 1; i < intervals.size(); i++)
{
if(result.back()[1] >= intervals[i][0])
{
result.back()[1] = max(result.back()[1],intervals[i][1]);
}
else result.push_back(intervals[i]);
}
return result;
}
};
738 单调递增的数字
题目链接
思路
先将数字按位转为字符串,需要用到to_string()和stoi()函数;思考局部最优:当前一位比后一位大时,将前一位减1,后一位变为9,此时为最大。这里需要注意:倘若直接变,之后不管的话,可能会出现前一位和后一位相同,更前一位比后一位大的情况,如100.所以需要记录需要变化的位置,将之后的所有数字变成9.
文章详解
cpp
class Solution {
public:
int monotoneIncreasingDigits(int n) {
string strnum = to_string(n);
int flag = strnum.size();
for(int i = strnum.size() - 1; i > 0 ; i--)
{
if(strnum[i-1] > strnum[i])
{
strnum[i-1]--;
flag = i;
}
//cout << strnum[i];
}
for(int i = flag; i < strnum.size();i++)
{
strnum[i] = '9';
}
return stoi(strnum);
}
};
968 监控二叉树
题目链接
思路
什么时候需要装摄像头?
- 左右子节点为叶子节点且也为0。
- 左孩子或者右孩子为空,另一个为叶子节点;
- 父节点存在且为0,左右孩子存在(不为叶子节点)且为0
- 父节点存在且为0,左或右孩子存在(不为叶子节点)且为0
采用什么遍历顺序?
上述情况都要考虑左右孩子,可以使用先根遍历,同时设定一个指针指向其父节点。当满足上述情况时,该节点的值设为1,即被安装摄像头。
初版代码如下:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int num;
void travelling(TreeNode* node, TreeNode* father)
{
// if(node->left == nullptr && node->right == nullptr)
// {
// return;
// }
if(node == nullptr) return;
else if(node->left && node->left->left == nullptr && node->left->right == nullptr &&
node->right && node->right->left == nullptr && node->right->right == nullptr)
//1.左右孩子均为叶子节点
{
if(node->val == 0)
{
node->val = 1;
num++;
}
return;
}
else if((node->left == nullptr && node->right && node->right->left == nullptr && node->right->right == nullptr)
|| (node->right == nullptr && node->left && node->left->left == nullptr && node->left->right == nullptr))
//2. 左孩子或者右孩子为空,另一个为叶子节点;
{
//cout << "!" << endl;
if(node->val == 0)
{
node->val = 1;
num++;
}
return;
}
else if(( node->right && node->right->left == nullptr && node->right->right == nullptr)
|| ( node->left && node->left->left == nullptr && node->left->right == nullptr))
//2. 左孩子或者右孩子为空,另一个为叶子节点;
{
//cout << "!" << endl;
if(node->val == 0)
{
node->val = 1;
num++;
}
//return;
}
else if((father && father->val == 0) && (node->left || node->right)) //父节点存在且为0,左右孩子存在(不为叶子节点)且为0
{
if(node->val == 0)
{
node->val = 1;
num++;
}
//return;
//return;
}
travelling(node->left,node);
travelling(node->right,node);
}
int minCameraCover(TreeNode* root) {
if(root == nullptr) return 0;
if(root->left == nullptr && root->right == nullptr) return 1;
travelling(root,nullptr);
return num;
}
};
无法通过所有测试集。从根节点往下需要考虑的太多。
这时我们转换思维:可不可以从下往上呢?我们给叶子节点的父节点安装摄像头,不给叶子节点安装,之后每个叶子节点向上每隔2个节点安装一个摄像头,保证充分利用!
那么如何每隔两个节点安装呢?
首先节点有3个状态:
- 无覆盖
- 有覆盖
- 有摄像头
分别表示为0,1,2.
而空节点的状态为有覆盖,这样叶子节点的父节点会安装摄像头
本层递归逻辑?
情况1:左右孩子都有覆盖
情况2:左右节点至少有一个无覆盖的情况
如果是以下情况,则中间节点(父节点)应该放摄像头:
left == 0 && right == 0 左右节点无覆盖
left == 1 && right == 0 左节点有摄像头,右节点无覆盖
left == 0 && right == 1 左节点有无覆盖,右节点摄像头
left == 0 && right == 2 左节点无覆盖,右节点覆盖
left == 2 && right == 0 左节点覆盖,右节点无覆盖
情况3:左右节点至少有一个有摄像头
如果是以下情况,其实就是 左右孩子节点有一个有摄像头了,那么其父节点就应该是2(覆盖的状态)
left == 1 && right == 2 左节点有摄像头,右节点有覆盖
left == 2 && right == 1 左节点有覆盖,右节点有摄像头
left == 1 && right == 1 左右节点都有摄像头
情况4:头结点没有覆盖
以上都处理完了,递归结束之后,可能头结点 还有一个无覆盖的情况
文章详解
cpp
// 版本一
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;
}
};