leecode更新

This commit is contained in:
markilue 2023-02-13 14:31:02 +08:00
parent 4b8e8a7d65
commit 8c613c918d
6 changed files with 318 additions and 2 deletions

View File

@ -16,7 +16,7 @@ import java.util.List;
* 以数组 intervals 表示若干个区间的集合其中单个区间为 intervals[i] = [starti, endi] 请你合并所有重叠的区间并返回 一个不重叠的区间数组该数组需恰好覆盖输入中的所有区间 * 以数组 intervals 表示若干个区间的集合其中单个区间为 intervals[i] = [starti, endi] 请你合并所有重叠的区间并返回 一个不重叠的区间数组该数组需恰好覆盖输入中的所有区间
* @Version: 1.0 * @Version: 1.0
*/ */
public class Merge { public class T12_Merge {
@Test @Test
public void test() { public void test() {

View File

@ -12,7 +12,7 @@ import org.junit.Test;
* 给定一个整数 n 返回 小于或等于 n 的最大数字且数字呈 单调递增 * 给定一个整数 n 返回 小于或等于 n 的最大数字且数字呈 单调递增
* @Version: 1.0 * @Version: 1.0
*/ */
public class MonotoneIncreasingDigits { public class T13_MonotoneIncreasingDigits {
@Test @Test
public void test() { public void test() {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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]);
}
}

View File

@ -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));
}
}