diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/Merge.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T12_Merge.java similarity index 99% rename from Leecode/src/main/java/com/markilue/leecode/greedy/Merge.java rename to Leecode/src/main/java/com/markilue/leecode/greedy/T12_Merge.java index 8ead320..2b6a26d 100644 --- a/Leecode/src/main/java/com/markilue/leecode/greedy/Merge.java +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T12_Merge.java @@ -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() { diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/MonotoneIncreasingDigits.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T13_MonotoneIncreasingDigits.java similarity index 99% rename from Leecode/src/main/java/com/markilue/leecode/greedy/MonotoneIncreasingDigits.java rename to Leecode/src/main/java/com/markilue/leecode/greedy/T13_MonotoneIncreasingDigits.java index 60eb359..8556922 100644 --- a/Leecode/src/main/java/com/markilue/leecode/greedy/MonotoneIncreasingDigits.java +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T13_MonotoneIncreasingDigits.java @@ -12,7 +12,7 @@ import org.junit.Test; * 给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。 * @Version: 1.0 */ -public class MonotoneIncreasingDigits { +public class T13_MonotoneIncreasingDigits { @Test public void test() { diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/T14_MaxProfit.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T14_MaxProfit.java new file mode 100644 index 0000000..ef1144b --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T14_MaxProfit.java @@ -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){ + sum += p - buy;//假装排除计算收益 + buy = p;//后期如果有数比他大,那么就是计算加上的p-buy增值收益 + } + } + return sum; + + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/T15_MinCameraCover.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T15_MinCameraCover.java new file mode 100644 index 0000000..c3228d8 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T15_MinCameraCover.java @@ -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; + } +} \ No newline at end of file diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/second/T12_Merge.java b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T12_Merge.java new file mode 100644 index 0000000..f4814ba --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T12_Merge.java @@ -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 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]); + + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/second/T13_MonotoneIncreasingDigits.java b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T13_MonotoneIncreasingDigits.java new file mode 100644 index 0000000..e657ca5 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T13_MonotoneIncreasingDigits.java @@ -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 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)); + } + +}