leecode更新
This commit is contained in:
parent
4b8e8a7d65
commit
8c613c918d
|
|
@ -16,7 +16,7 @@ import java.util.List;
|
|||
* 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class Merge {
|
||||
public class T12_Merge {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
|
@ -12,7 +12,7 @@ import org.junit.Test;
|
|||
* 给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class MonotoneIncreasingDigits {
|
||||
public class T13_MonotoneIncreasingDigits {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package com.markilue.leecode.greedy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*@BelongsProject: Leecode
|
||||
*@BelongsPackage: com.markilue.leecode.greedy
|
||||
*@Author: markilue
|
||||
*@CreateTime: 2023-02-13 11:12
|
||||
*@Description:
|
||||
* TODO 力扣714题 买卖股票的最佳时期含手续费:
|
||||
* 给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。
|
||||
* 你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
|
||||
* 返回获得利润的最大值。
|
||||
* 注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
|
||||
*@Version: 1.0
|
||||
*/
|
||||
public class T14_MaxProfit {
|
||||
|
||||
@Test
|
||||
public void test(){
|
||||
int[] prices = {1, 3, 2, 8, 7, 9};
|
||||
int fee = 2;
|
||||
System.out.println(maxProfit2(prices,fee));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 动态规划法思路:
|
||||
* TODO dp五部曲:
|
||||
* 1.dp含义: dp[i][0]表示手里没有股票的最大收益,dp[i][1]表示手里有股票的最大收益
|
||||
* 2.dp状态转移方程:
|
||||
* 1.dp[i][0] :手里没有股票的 昨天没有今天也没有;昨天有了今天卖了
|
||||
* dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee)
|
||||
* 1.dp[i][1] :手里有股票的 昨天没有今天有没操作;昨天没有今天买了
|
||||
* dp[i][0]=max(dp[i-1][1],dp[i-1][0]-prices[i])
|
||||
* 3.dp初始化:dp[0][0]=0;dp[0][1]=-price[i]
|
||||
* 4.dp遍历顺序:从前往后
|
||||
* 5.dp举例推导:以prices = [1, 3, 2, 8, 4, 9], fee = 2为例:
|
||||
* [0 1]
|
||||
* i=1: 0 -1
|
||||
* i=3: 0 -1
|
||||
* i=2: 0 -1
|
||||
* i=8: 5 -1
|
||||
* i=4: 5 1
|
||||
* i=9: 8 1
|
||||
* 二维dp 速度击败49.37%,内存击败96.1% 18ms
|
||||
* @param prices
|
||||
* @param fee
|
||||
* @return
|
||||
*/
|
||||
public int maxProfit(int[] prices, int fee) {
|
||||
|
||||
int[][] dp = new int[prices.length][2];
|
||||
dp[0][0] = 0;
|
||||
dp[0][1] = -prices[0];
|
||||
|
||||
for (int i = 1; i < prices.length; i++) {
|
||||
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);
|
||||
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
|
||||
}
|
||||
return dp[dp.length - 1][0];
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 一维dp优化
|
||||
* 速度击败61.13% 内存击败20.14% 5ms
|
||||
* @param prices
|
||||
* @param fee
|
||||
* @return
|
||||
*/
|
||||
public int maxProfit1(int[] prices, int fee) {
|
||||
|
||||
int dp0 = 0;
|
||||
int dp1 = -prices[0];
|
||||
|
||||
for (int i = 1; i < prices.length; i++) {
|
||||
dp0 = Math.max(dp0, dp1 + prices[i] - fee);
|
||||
dp1 = Math.max(dp1, dp0 - prices[i]);
|
||||
}
|
||||
return dp0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 贪心算法思路:
|
||||
* 速度击败99.95%,内存击败5.14% 3ms
|
||||
* @param prices
|
||||
* @param fee
|
||||
* @return
|
||||
*/
|
||||
public int maxProfit2(int[] prices, int fee) {
|
||||
|
||||
int buy = prices[0] + fee;
|
||||
int sum = 0;
|
||||
for (int p : prices) {
|
||||
if (p + fee < buy) {//后续如果比8大的数,如果在之前遇到了p + fee<buy的情况,那么后续就一定有正收益了,所以之前的可以放心卖
|
||||
buy = p + fee;//寻找最低价格
|
||||
} else if (p > buy){
|
||||
sum += p - buy;//假装排除计算收益
|
||||
buy = p;//后期如果有数比他大,那么就是计算加上的p-buy增值收益
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package com.markilue.leecode.greedy;
|
||||
|
||||
import com.markilue.leecode.tree.TreeNode;
|
||||
|
||||
/**
|
||||
*@BelongsProject: Leecode
|
||||
*@BelongsPackage: com.markilue.leecode.greedy
|
||||
*@Author: markilue
|
||||
*@CreateTime: 2023-02-13 13:19
|
||||
*@Description:
|
||||
* TODO 力扣968题 监控二叉树:
|
||||
* 给定一个二叉树,我们在树的节点上安装摄像头。
|
||||
* 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
|
||||
* 计算监控树的所有节点所需的最小摄像头数量。
|
||||
*@Version: 1.0
|
||||
*/
|
||||
public class T15_MinCameraCover {
|
||||
|
||||
/**
|
||||
* 代码随想录解法:将当前节点的状态定为三种:未覆盖,被覆盖,有摄像头;根据子节点状态,推断当前节点状态
|
||||
* 这是基础解法,如何做到最少呢?贪心:叶子节点不放;根节点不放,这样一个摄像头就可以顾及上中下三层
|
||||
* 状态定义:
|
||||
* 0:该节点无覆盖
|
||||
* 1:本节点有摄像头
|
||||
* 2:本节点有覆盖
|
||||
* 速度击败100%,内存击败78.43%
|
||||
*/
|
||||
int result=0;
|
||||
public int minCameraCover(TreeNode root) {
|
||||
// 情况4
|
||||
if (traversal(root) == 0) { // root 无覆盖
|
||||
result++;
|
||||
}
|
||||
return 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.markilue.leecode.greedy.second;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*@BelongsProject: Leecode
|
||||
*@BelongsPackage: com.markilue.leecode.greedy.second
|
||||
*@Author: markilue
|
||||
*@CreateTime: 2023-02-13 10:05
|
||||
*@Description:
|
||||
* TODO 力扣56题 合并区间:
|
||||
* 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。
|
||||
* 请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
|
||||
*@Version: 1.0
|
||||
*/
|
||||
public class T12_Merge {
|
||||
|
||||
@Test
|
||||
public void test(){
|
||||
int[][] intervals={{1,3},{2,6},{8,10},{15,18}};
|
||||
int[][] merge = merge(intervals);
|
||||
for (int[] interval : merge) {
|
||||
System.out.println(Arrays.toString(interval));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 思路:本质上也是找到重叠区间,然后将其合并即可,核心在于具体还剩多少个无法确定,因此用List装
|
||||
* @param intervals
|
||||
* @return
|
||||
*/
|
||||
public int[][] merge(int[][] intervals) {
|
||||
|
||||
if(intervals.length==1){
|
||||
return intervals;
|
||||
}
|
||||
|
||||
List<int[]> result = new ArrayList<>();
|
||||
Arrays.sort(intervals, (a, b) -> {
|
||||
return a[0] - b[0];
|
||||
});
|
||||
|
||||
int threshold = intervals[0][1];
|
||||
int thresholdMin = intervals[0][0];
|
||||
for (int i = 1; i < intervals.length; i++) {
|
||||
if (intervals[i][0] <= threshold) {
|
||||
threshold = Math.max(threshold, intervals[i][1]);
|
||||
} else {
|
||||
//之前的都合并完了,把结果加入结果集
|
||||
result.add(new int[]{thresholdMin, threshold});
|
||||
thresholdMin = intervals[i][0];
|
||||
threshold = intervals[i][1];
|
||||
}
|
||||
}
|
||||
//最后一步了,无论如何都要加入结果了了
|
||||
result.add(new int[]{thresholdMin, threshold});
|
||||
|
||||
return result.toArray(new int[1][1]);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.markilue.leecode.greedy.second;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*@BelongsProject: Leecode
|
||||
*@BelongsPackage: com.markilue.leecode.greedy.second
|
||||
*@Author: markilue
|
||||
*@CreateTime: 2023-02-13 10:42
|
||||
*@Description:
|
||||
* TODO 力扣738题 单调递增的数字:
|
||||
* 当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。
|
||||
* 给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。
|
||||
*@Version: 1.0
|
||||
*/
|
||||
public class T13_MonotoneIncreasingDigits {
|
||||
|
||||
public int monotoneIncreasingDigits(int n) {
|
||||
|
||||
List<Integer> list = new ArrayList<>();//[个,十,百,千],所以让前面的数尽可能大
|
||||
|
||||
while (n != 0) {
|
||||
list.add(n % 10);
|
||||
n /= 10;
|
||||
}
|
||||
|
||||
for (int i = 1; i < list.size(); i++) {
|
||||
if (list.get(i) > list.get(i - 1)) {
|
||||
list.set(i, list.get(i) - 1);
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
list.set(j, 9);
|
||||
}
|
||||
}
|
||||
}
|
||||
int result = 0;
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
result = result * 10 + list.get(i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 官方的极度贪心,可以避免我的多次内部for的情况;找到不合适的位置,就往前找,一次性把后面全部设置为9
|
||||
* @param n
|
||||
* @return
|
||||
*/
|
||||
public int monotoneIncreasingDigits1(int n) {
|
||||
char[] strN = Integer.toString(n).toCharArray();
|
||||
int i = 1;
|
||||
while (i < strN.length && strN[i - 1] <= strN[i]) {
|
||||
i += 1;
|
||||
}
|
||||
if (i < strN.length) {
|
||||
while (i > 0 && strN[i - 1] > strN[i]) {
|
||||
strN[i - 1] -= 1;
|
||||
i -= 1;
|
||||
}
|
||||
for (i += 1; i < strN.length; ++i) {
|
||||
strN[i] = '9';
|
||||
}
|
||||
}
|
||||
return Integer.parseInt(new String(strN));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue