From 5c54b530f43dfdb75612282de1219fb8b302a594 Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Thu, 9 Feb 2023 18:58:41 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../greedy/T07_LargestSumAfterKNegations.java | 208 ++++++++++++++++++ ...rcuit.java => T08_CanCompleteCircuit.java} | 2 +- .../greedy/{Candy.java => T09_Candy.java} | 2 +- .../greedy/second/T08_CanCompleteCircuit.java | 129 +++++++++++ .../leecode/greedy/second/T09_Candy.java | 22 ++ 5 files changed, 361 insertions(+), 2 deletions(-) create mode 100644 Leecode/src/main/java/com/markilue/leecode/greedy/T07_LargestSumAfterKNegations.java rename Leecode/src/main/java/com/markilue/leecode/greedy/{CanCompleteCircuit.java => T08_CanCompleteCircuit.java} (99%) rename Leecode/src/main/java/com/markilue/leecode/greedy/{Candy.java => T09_Candy.java} (99%) create mode 100644 Leecode/src/main/java/com/markilue/leecode/greedy/second/T08_CanCompleteCircuit.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/greedy/second/T09_Candy.java diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/T07_LargestSumAfterKNegations.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T07_LargestSumAfterKNegations.java new file mode 100644 index 0000000..fe58756 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T07_LargestSumAfterKNegations.java @@ -0,0 +1,208 @@ +package com.markilue.leecode.greedy; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.stream.IntStream; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.greedy + *@Author: markilue + *@CreateTime: 2023-02-09 10:37 + *@Description: + * TODO 力扣1005 K 次取反后最大化的数组和: + * 给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组: + * 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 + * 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 + * 以这种方式修改数组后,返回数组 可能的最大和 。 + *@Version: 1.0 + */ +public class T07_LargestSumAfterKNegations { + + + @Test + public void test() { +// int[] nums = {4, 2, 3}; +// int[] nums = {3,-1,0,2}; +// int[] nums = {2,-3,-1,5,-4}; + int[] nums = {-3,-1,-4,2}; +// int[] nums = {-2,5,0,2,-2}; + System.out.println(largestSumAfterKNegations(nums, 5)); + } + + /** + * 思路:分情况讨论:花了很长时间才写出,条件判断太复杂 + * 1)如果负数数量>=k,那么直接取反负数 + * 2)如果负数数量 Math.abs(o2) - Math.abs(o1)) + .mapToInt(Integer::intValue).toArray(); + int len = nums.length; + for (int i = 0; i < len; i++) { + //从前向后遍历,遇到负数将其变为正数,同时K-- + if (nums[i] < 0 && K > 0) { + nums[i] = -nums[i]; + K--; + } + } + // 如果K还大于0,那么反复转变数值最小的元素,将K用完,把绝对值最小的反过来 + if (K % 2 == 1) nums[len - 1] = -nums[len - 1]; + return Arrays.stream(nums).sum(); + + } + + + /** + * 胆码随想录另一种思路 + * 速度击败97.8%,内存击败24.17% + * @param A + * @param k + * @return + */ + public int largestSumAfterKNegations2(int[] A, int k) { + if (A.length == 1) return k % 2 == 0 ? A[0] : -A[0]; + Arrays.sort(A); + int sum = 0; + int idx = 0; + for (int i = 0; i < k; i++) { + if (i < A.length - 1 && A[idx] < 0) { + A[idx] = -A[idx]; + if (A[idx] >= Math.abs(A[idx + 1])) idx++; + continue; + } + A[idx] = -A[idx]; + } + + for (int i = 0; i < A.length; i++) { + sum += A[i]; + } + return sum; + } + + + /** + * 评论区对于代码随想录的优化:使用两次排序找到最小的值最后进行反转优化 + * @param nums + * @param k + * @return + */ + public int largestSumAfterKNegations3(int[] nums, int k) { + // 排序,把可能有的负数排到前面 + Arrays.sort(nums); + int sum = 0; + for (int i = 0; i < nums.length; i++) { + // 贪心:如果是负数,而k还有盈余,就把负数反过来 + if (nums[i] < 0 && k > 0) { + nums[i] = -1 * nums[i]; + k--; + } + sum += nums[i]; + } + Arrays.sort(nums); + // 如果k没剩,那说明能转的负数都转正了,已经是最大和,返回sum + // 如果k有剩,说明负数已经全部转正,所以如果k还剩偶数个就自己抵消掉,不用删减,如果k还剩奇数个就减掉2倍最小正数。 + return sum - (k % 2 == 0 ? 0 : 2 * nums[0]); + } + + /** + * 官方最快:0ms,利用count记录数量,整体处理,如果非要找一个,可以根据数值直接索引来找 + * 速度击败100%,内存击败85.23% 0ms + * @param nums + * @param k + * @return + */ + public int largestSumAfterKNegations4(int[] nums, int k) { + int[] count = new int[201]; + int sum = 0; + for(int i : nums) { + ++ count[i + 100]; + sum += i; + } + + // 优先反转负数 + for(int i = -100; i < 0 && k > 0; ++ i) { + if(count[100 + i] == 0) continue; + // 有负数 + int times = Math.min(k, count[i + 100]); + k -= times; + sum -= (times * 2 * i); + } + + // 再选一个必须反转的数 + if(k > 0 && (k & 1) == 1) { + for(int i = 100, j = 100; i < 201; ++ i, -- j) { + if(count[i] > 0) { + sum -= (i - 100) << 1; + break; + } + if(count[j] > 0) { + sum += (j - 100) << 1; + break; + } + } + } + return sum; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/CanCompleteCircuit.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T08_CanCompleteCircuit.java similarity index 99% rename from Leecode/src/main/java/com/markilue/leecode/greedy/CanCompleteCircuit.java rename to Leecode/src/main/java/com/markilue/leecode/greedy/T08_CanCompleteCircuit.java index 9c7a4f7..c20fd8f 100644 --- a/Leecode/src/main/java/com/markilue/leecode/greedy/CanCompleteCircuit.java +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T08_CanCompleteCircuit.java @@ -13,7 +13,7 @@ import org.junit.Test; * 给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。 * @Version: 1.0 */ -public class CanCompleteCircuit { +public class T08_CanCompleteCircuit { @Test public void test() { diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/Candy.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T09_Candy.java similarity index 99% rename from Leecode/src/main/java/com/markilue/leecode/greedy/Candy.java rename to Leecode/src/main/java/com/markilue/leecode/greedy/T09_Candy.java index 1e106a5..dc5ed51 100644 --- a/Leecode/src/main/java/com/markilue/leecode/greedy/Candy.java +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T09_Candy.java @@ -17,7 +17,7 @@ import java.util.Arrays; * 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。 * @Version: 1.0 */ -public class Candy { +public class T09_Candy { @Test public void test() { diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/second/T08_CanCompleteCircuit.java b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T08_CanCompleteCircuit.java new file mode 100644 index 0000000..ea43501 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T08_CanCompleteCircuit.java @@ -0,0 +1,129 @@ +package com.markilue.leecode.greedy.second; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.greedy.second + *@Author: markilue + *@CreateTime: 2023-02-09 12:04 + *@Description: + * TODO 力扣134题 加油站: + * 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 + * 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 + * 给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。 + *@Version: 1.0 + */ +public class T08_CanCompleteCircuit { + + @Test + public void test() { + int[] gas = {2, 3, 4}, cost = {3, 4, 3}; + System.out.println(canCompleteCircuit(gas, cost)); + } + + + /** + * 思路:挨个找,基本等同于暴力解法,时间超出限制 + * @param gas + * @param cost + * @return + */ + public int canCompleteCircuit(int[] gas, int[] cost) { + + for (int i = 0; i < gas.length; i++) { + if (gas[i] < cost[i]) continue; + //看看这个位置到不到不得了 + int gasSum = 0; + for (int j = 0; j <= gas.length; j++) { + if (j == gas.length) return i; + int left = gas[(j + i) % gas.length] - cost[(j + i) % gas.length]; + if (gasSum + left < 0) break; + gasSum += left; + } + } + + return -1;//遍历完了都不行,返回-1 + + } + + + /** + * 思路:对上面思路的优化,如果记录下break的位置之后,那么之前的则可以全部跳过 + * 速度击败25.59%,内存击败61.45% 3ms + * @param gas + * @param cost + * @return + */ + public int canCompleteCircuit1(int[] gas, int[] cost) { + + for (int i = 0; i < gas.length; i++) { + if (gas[i] < cost[i]) continue; + //看看这个位置到不到不得了 + int gasSum = 0; + int j = 0; + for (; j <= gas.length; j++) { + if (j == gas.length) return i; + int left = gas[(j + i) % gas.length] - cost[(j + i) % gas.length]; + if (gasSum + left < 0) break; + gasSum += left; + } + i = i + j;//这一段不用再遍历了 + } + + return -1;//遍历完了都不行,返回-1 + + } + + + /** + * 以前的思路:直接用totalSum和curSum记录当前的总和,如果curSum小于0了,那么就证明之前一定到不了, + * index一定在最后面,接着最后判断totalSum的总和来判断到底到不了的了 + * 只用遍历一次 + * 速度击败81.71%,内存击败81.5% 2ms + * @param gas + * @param cost + * @return + */ + public int canCompleteCircuit2(int[] gas, int[] cost) { + int curSum = 0; + int totalSum = 0; + int start = 0; + + for (int i = 0; i < gas.length; i++) { + + curSum += gas[i] - cost[i]; + totalSum += gas[i] - cost[i]; + if (curSum < 0) { + start = i + 1; + curSum = 0; + } + } + if (totalSum < 0) return -1; + return start; + } + + /** + * 官方最快:本质上也是贪心,找到局部最小值 + * 速度击败100%,内存击败80.24% + * @param gas + * @param cost + * @return + */ + public int canCompleteCircuit3(int[] gas, int[] cost) { + int n = gas.length; + int gasVal = 0; + int minVal = Integer.MAX_VALUE; + int minIndex = 0; + for (int i = 0; i < n; i++) { + gasVal += gas[i] - cost[i]; + if (gasVal < minVal) { + minVal = gasVal; + minIndex = i; + } + } + if (gasVal < 0) return -1; + + return (minIndex + 1) % n;//一定在最小的位置的后面1个 + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/second/T09_Candy.java b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T09_Candy.java new file mode 100644 index 0000000..baa1075 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T09_Candy.java @@ -0,0 +1,22 @@ +package com.markilue.leecode.greedy.second; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.greedy.second + *@Author: markilue + *@CreateTime: 2023-02-09 13:12 + *@Description: + * TODO 力扣135题 分发糖果: + * n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 + * 你需要按照以下要求,给这些孩子分发糖果: + * 每个孩子至少分配到 1 个糖果。 + * 相邻两个孩子评分更高的孩子会获得更多的糖果。 + * 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。 + *@Version: 1.0 + */ +public class T09_Candy { + + public int candy(int[] ratings) { + + } +}