所用代码 java
单调递增的数字 LeetCode 738
题目链接:单调递增的数字 LeetCode 738 - 中等
思路
无。题都没看懂
题目的意思是:
给一个数如:123
每个位数的数字是 1 2 3
这三个数字是单调递增的,所以返回小于或等于123的单调递增的数字为 123
如给一个数 10 ,每个数字为 1 0 这就不是单调递增的,返回的小于或等于10的单调递增的数字为 9
给定一个数如32,若该数不是单调递增的就把前一位数减1,后一位取最大值9,就是29,该题就是单递增的。
第二个需注意的就是从后往前遍历较为方便。
class Solution {
public int monotoneIncreasingDigits(int n) {
// 先转为char数组方便对每一位数处理
String str = String.valueOf(n);
char[] c = str.toCharArray();
// flag标记之后,把后面的数都置为9
int flag = c.length;
// 从后往前遍历,可减少比较次数
for (int i = c.length - 1; i > 0 ; i--) {
// 非单调递增就让该数-1,并记录标记位
if (c[i-1] > c[i]){
c[i-1]--;
flag = i;
}
}
for (int i = flag; i < c.length; i++) {
c[i] = '9';
}
// char数组转int
return Integer.parseInt(String.valueOf(c));
}
}
总结
本题有几个重点:
- 为什么需要flag:若数是 1000,最大单调递增则是999,没有flag直接赋值的话,千位的 1<0,1变成0,0变成9,其他位不变,该数就变成了900。
int flag = c.length;
等于数组长度是为了防止原数组本来就是单调递增的,又重新去赋值了
本题可以暴力,但是超时了!:
class Solution {
public int monotoneIncreasingDigits(int n) {
// 从大到小遍历
for (int i = n; i >= 0; i--) {
if (checkNum(i)) return i;
}
return -1;
}
// 判断是否是单调递增
public boolean checkNum(int num){
int max = 10;
while (num > 0){
int t = num % 10;
if (max >= t) max = t;
else return false;
num /= 10;
}
return true;
}
}
买股票的最佳时机含手续费 LeetCode 714
题目链接:买股票的最佳时机含手续费 LeetCode 714 - 中等
思路
无。
class Solution {
public int maxProfit(int[] prices, int fee) {
int sum = 0;
int minPrice = prices[0];
for (int i = 1; i < prices.length; i++) {
// 买入
if (prices[i] < minPrice) {
minPrice = prices[i];
}
// 此时卖出并不赚钱
if (prices[i] >= minPrice && prices[i] <= minPrice + fee){
continue;
}
// 有利润则卖出,实际为继续持股,后面继续收集利润
if (prices[i] > minPrice + fee){
sum += prices[i] - minPrice - fee;
// 防止多减了一次利润 补的一个值
minPrice = prices[i] - fee;
}
}
return sum;
}
}
总结
重点在于为什么会有这一步: minPrice = prices[i] - fee;
若prices[1,7,8] fee=2,我们在price=7的时候就卖出 sum= 7 - 1- 2 = 4,但此时并不是最佳的利润,最佳利润应该是price=8时,sum = 8 - 1 - 2 = 5。
所以当我们在price=7时,获得的利润并不是真正的利润,但是已经加了一次利润了,所以在第二次更新利润的时候不能再减去手续费了,即求和利润中的 prices[i+1] - minPrice - fee
需将之前的最小利润minPrice替换为minPrice = prices[i] - fee
带入第一个式子即:prices[i+1] - (prices[i] - fee) - fee = prices[i+1] - prices[i]
。
所以当我们在price=7获得了一次利润sum= 7 - 1- 2 = 4,然后在利润更高的点price=8更新一次更高的利润和,即后一天价格减前一个价格 prices[i+1] - prices[i]
。
监控二叉树 LeetCode 968
题目链接:监控二叉树 LeetCode 968 - 困难
思路
无。
一个摄像头可以覆盖上中下,首先要考虑叶子结点的摄像头,需叶子结点的父节点去放,就可以考虑多个,而叶子结点的话就应使用后序(左右中)。
难点在于隔一个结点放摄像头,我们把结点状态分为三种:
- 0 无覆盖
- 1 有摄像头
- 2 有覆盖 – 空结点有覆盖
所以我们可以分为四种情况:
- 左右孩子都有覆盖,父节点无覆盖
- 左右还是至少有一个无覆盖,父节点必装摄像头
- 左右孩子至少有一个有摄像头,父节点有覆盖
- 根结点为无覆盖状态,根结点需放一个摄像头
class Solution {
int result;
public int minCameraCover(TreeNode root) {
result = 0;
if (traversal(root) == 0) result++;
return result;
}
public int traversal(TreeNode node){
// 空结点默认为有覆盖
if (node == null) return 2;
int left = traversal(node.left);
int right = traversal(node.right);
// 1、左右都有覆盖的话,那么本结点都是无覆盖
if (left == 2 && right == 2) return 0;
// 2、左右结点其中一个无覆盖的话,应该装摄像头
if (left == 0 || right == 0) {
result++;
return 1;
}
// 3、左右结点其中一个有摄像头的话,本结点就是有覆盖
if (left == 1 || right == 1) return 2;
// 这个结果不会走,上面已经包括完了
return -999;
}
}
总结
本题其中有一个重点就是,按这个思路不是if else的话,顺序不能出错,必须按个按照上面的顺序。中结点的处理顺序为:
- 先判断左右是不是都有覆盖(叶子结点)
- 若出现根结点左边一个叶子结点,右边两个这种情况,根节点左右就是一个无覆盖一个装了摄像头,必须按这个顺序才会在根结点装一个摄像头,否则先走3再走2就被判断有覆盖会漏掉一个。
- 最后才是左右其中一个有摄像头。
注意: 上面三种情况已囊括了返回值为:0 1 2的所有情况,不会最后走到返回-999的情况了。