LeetCode 738.单调递增的数字
思路:
为了找到最大的不大于n的单调递增的数字,我们可以采用以下贪心策略:当n不为单调递增数字时,把n的前一位减1,并把后一位改成9,反复进行如上操作直到n为单调递增,即可保证结果最大。
检测是否为单调递增的函数。先从个位数取10的模,记录下当前的最大值,然后把n缩小10倍再取模,如果大于之前记录的最大值则说明不是单调递增,循环直到n的每一位数都取了模。
bool checkIncrease(int n)
{
int MAX = 9;
while (n)
{
int digit = n % 10;
if (digit <= MAX)
MAX = digit;
else
return false;
n /= 10;
}
return true;
}
执行贪心策略的核心代码。需要一个变量pos来表示在哪一位上进行减1操作,表示从右往左数的第pos位,需要注意的是在该位置的数字必须大于0,因为是对字符串之间操作减法的,否则会出错。
int greedy(int n, int pos)
{
string num = to_string(n);
if (num[num.size() - pos - 1] > '0')
num[num.size() - pos - 1] --;
num[num.size() - pos] = '9';
return stoi(num);
}
完整代码:
class Solution {
public:
int monotoneIncreasingDigits(int n) {
if (n < 10)
return n;
int pos = 0;
while(!checkIncrease(n))
{
pos++;
n = greedy(n, pos);
}
return n;
}
bool checkIncrease(int n)
{
int MAX = 9;
while (n)
{
int digit = n % 10;
if (digit <= MAX)
MAX = digit;
else
return false;
n /= 10;
}
return true;
}
int greedy(int n, int pos)
{
string num = to_string(n);
if (num[num.size() - pos - 1] > '0')
num[num.size() - pos - 1] --;
num[num.size() - pos] = '9';
return stoi(num);
}
};
LeetCode 968.监控二叉树
思路:
这道题是名副其实的hard题,在看代码随想录之前一点思路也没有,虽然贪心策略很明确,就是每隔一层放置一个摄像头,叶子节点不放摄像头,但是用代码实际操作起来还是不容易的。首先我们需要给每一个节点都定义一个状态,一个节点可能的状态有以下三种:
0:未覆盖
1:有覆盖
2:有摄像头
然后根据以上三种状态,给每一个节点定义一个状态转移公式。假设我们从数的底层往上层填写状态,也就是从下往上摆摄像头,所以可以用后序遍历的方式先遍历左右节点,再遍历中间节点。后序遍历的递归函数是有返回值的,返回该节点的状态,中间节点的状态可以根据左右节点返回的状态来决定。根据左右节点不同的状态,分为以下三种情况:
1、左节点和右节点均有覆盖,中间节点无覆盖
if (left == 1 && right == 1)
return 0;
2、如果左节点或者右节点无覆盖,中间节点有摄像头
else if (left == 0 || right == 0)
{
count++;
return 2;
}
注意必须要先写这个逻辑,因为要先判断有无覆盖的情况。有可能会出现左或右节点有摄像头但是另一个节点无覆盖的情况,这种时候还是需要摄像头在该节点。比如这种情况:[0,0,0,null,null,null,0]
3、如果左节点或者右节点有摄像头,中间节点有覆盖
else if (left == 2 || right == 2)
return 1;
递归返回的条件为遇到空节点。那么遇到空节点应该返回什么呢?如果返回0,则说明未覆盖,中间节点(即叶子节点)就要放摄像头了,但我们不会在叶子节点放摄像头,所以不能返回0。如果返回2,则叶子节点为有覆盖状态,叶子节点的父节点为未覆盖状态,这个时候叶子节点的祖父节点才能放摄像头,也不是我们想要的。所以只能返回1,空节点为有覆盖状态,叶子节点未覆盖,这个时候就可以在叶子节点的父节点放摄像头了。
最后,有可能会出现头节点没有覆盖的情况,这个时候需要在头节点再放一个摄像头。
// 最后还需要判断根节点是否覆盖
if(!postorder(root, count))
count++;
完整代码:
class Solution {
public:
int minCameraCover(TreeNode* root) {
int count = 0;
if(!postorder(root, count))
count++;
// 最后还需要判断根节点是否覆盖
return count;
}
int postorder(TreeNode* root, int &count)
{
// 0: 未覆盖
// 1: 有覆盖
// 2: 有摄像头
// 空节点的话,则视为有覆盖
if (root == nullptr)
return 1;
int left = postorder(root->left, count);
int right = postorder(root->right, count);
// 如果左节点和右节点均有覆盖,中间节点无覆盖
if (left == 1 && right == 1)
return 0;
// 如果左节点或者右节点无覆盖,中间节点有摄像头
// 必须要先写这个逻辑,因为要先判断有无覆盖的情况
// 有可能会出现左或右节点有摄像头但是另一个节点无覆盖的情况,这种时候还是需要摄像头在该节点
// 比如这种情况:[0,0,0,null,null,null,0]
else if (left == 0 || right == 0)
{
count++;
return 2;
}
// 如果左节点或者右节点有摄像头,中间节点有覆盖
else if (left == 2 || right == 2)
return 1;
else
// not reached
return -1;
}
};