738.单调递增的数字
思路:从后向前遍历,如果前一位大于当前位,就让前一位减一,记录当前位的下标,遍历结束后,从记录的下标开始往后都要变成9。
局部最优:遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]--,然后strNum[i]给为9,可以保证这两位变成最大单调递增整数。
全局最优:得到小于等于N的最大单调递增的整数。
class Solution {
public int monotoneIncreasingDigits(int n) {
// 转数组
int[] arr = getArr(n);
int start = arr.length;
for (int i = arr.length - 1; i > 0; i--) {
if (arr[i] < arr[i - 1]) {
arr[i - 1] -= 1;
start = i;
}
}
for (int i = start; i < arr.length; i++) {
arr[i] = 9;
}
// 转int类型
return getInt(arr);
}
public int[] getArr(int n) {
int len = 0;
int temp = n;
while (temp != 0) {
temp /= 10;
len++;
}
int[] arr = new int[len];
for (int i = len - 1; i >= 0; i--) {
arr[i] = n % 10;
n /= 10;
}
return arr;
}
public int getInt(int[] arr) {
int res = 0;
for (int i = 0; i < arr.length; i++) {
res += arr[i];
if (i != arr.length - 1) {
res *= 10;
}
}
return res;
}
}
这里也可以使用字符数组来进行操作
class Solution {
public int monotoneIncreasingDigits(int n) {
String s = String.valueOf(n);
char[] chars = s.toCharArray();
int start = s.length();
for (int i = s.length() - 2; i >= 0; i--) {
if (chars[i] > chars[i + 1]) {
chars[i]--;
start = i+1;
}
}
for (int i = start; i < s.length(); i++) {
chars[i] = '9';
}
return Integer.parseInt(String.valueOf(chars));
}
}
714. 买卖股票的最佳时机含手续费
思路:我们只需要确定买入日期和卖出日期即可,遍历数组,当遇到比当前最小值还要小的价格就更新买入日期,当遇到可以赚钱的时候就将其利润加入到结果中,并更新买入的最小值。
class Solution {
public int maxProfit(int[] prices, int fee) {
int res = 0;
int min = prices[0];
for (int i = 1; i < prices.length; i++) {
// 如果比最小值还小,更新买入的日期
if (prices[i] < min) {
min = prices[i];
}
// 如果能够赚到钱,更新总利润,更新最小值
if (prices[i] - min - fee > 0) {
res += prices[i] - min - fee;
min = prices[i] - fee; // 这里更新最小值要减去手续费,因为并不确定是否真正卖出。(后面可能还有更合适的,防止不必要的手续费)
}
// 如果买入卖出都不合适,什么也不做
}
return res;
}
}
968.监控二叉树
思路:我们可以找到规律,叶子节点是一定不能存放摄像头的,因为一个摄像头可以监测三层的节点,如果放在叶子节点上就少了一层,由此我们可以确定遍历顺序位自底向上的后序遍历。我们可以设0为当前节点未被监测到,1为当前节点为监控节点,2为当前节点已经被监测到。
class Solution {
private int count = 0;
public int minCameraCover(TreeNode root) { // 后序遍历
lastorder(root);
// 如果根节点没有被覆盖到,要加一个
if ((root.left == null || root.left.val == 2) && (root.right == null || root.right.val == 2)) count++;
return count;
}
public void lastorder(TreeNode node) {
if (node == null) return;
lastorder(node.left);
lastorder(node.right);
// 叶子节点直接返回
if (node.left == null && node.right == null) return;
// 如果有一个孩子节点有监控,设当前节点位2
if ((node.left != null && node.left.val == 1) || (node.right != null && node.right.val == 1)) {
node.val = 2;
}
// 如果有一个孩子没有被覆盖到,设当前节点为1,监控数量加一
if ((node.left != null && node.left.val == 0) || (node.right != null && node.right.val == 0)) {
node.val = 1;
count++;
}
// 当前节点的孩子都被覆盖到了,不用做操作。
}
}
随想录中解法,代码更简洁
class Solution {
int res=0;
public int minCameraCover(TreeNode root) {
// 对根节点的状态做检验,防止根节点是无覆盖状态 .
if(minCame(root)==0){
res++;
}
return res;
}
/**
节点的状态值:
0 表示无覆盖
1 表示 有摄像头
2 表示有覆盖
后序遍历,根据左右节点的情况,来判读 自己的状态
*/
public int minCame(TreeNode root){
if(root==null){
// 空节点默认为 有覆盖状态,避免在叶子节点上放摄像头
return 2;
}
int left=minCame(root.left);
int right=minCame(root.right);
// 如果左右节点都覆盖了的话, 那么本节点的状态就应该是无覆盖,没有摄像头
if(left==2&&right==2){
//(2,2)
return 0;
}else if(left==0||right==0){
// 左右节点都是无覆盖状态,那 根节点此时应该放一个摄像头
// (0,0) (0,1) (0,2) (1,0) (2,0)
// 状态值为 1 摄像头数 ++;
res++;
return 1;
}else{
// 左右节点的 状态为 (1,1) (1,2) (2,1) 也就是左右节点至少存在 1个摄像头,
// 那么本节点就是处于被覆盖状态
return 2;
}
}
}