leecode更新
This commit is contained in:
parent
c893db74ad
commit
5c54b530f4
|
|
@ -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)如果负数数量<k,分为(k-n)%2两种情况
|
||||
* 1.如果(k-n)%2==0,那么和1)一致
|
||||
* 2.如果(k-n)%2==1,看看负数最后一个和正数的第一个之和为正还是负
|
||||
* 速度击败97.8%,内存击败5.2% 2ms
|
||||
* @param nums
|
||||
* @param k
|
||||
* @return
|
||||
*/
|
||||
public int largestSumAfterKNegations(int[] nums, int k) {
|
||||
Arrays.sort(nums);
|
||||
//计算负数的个数
|
||||
int fu = 0;
|
||||
for (int i = 0; i < nums.length; i++) {
|
||||
if (nums[i] < 0) fu++;
|
||||
else break;
|
||||
}
|
||||
int result = 0;
|
||||
int index = 0;//需要一直取反的索引
|
||||
if (k <= fu) {
|
||||
index = k-1;//减1是因为要从个数变成索引
|
||||
} else {
|
||||
if ((k - fu) % 2 == 0 ) {//没有负数
|
||||
index = fu-1;
|
||||
} else {
|
||||
if(fu == 0) {
|
||||
index = 0;
|
||||
}else {
|
||||
//(k-n)%2==1的情况
|
||||
if (fu < nums.length && nums[fu-1] + nums[fu] < 0) {
|
||||
index = fu + 1-1;
|
||||
} else {
|
||||
index = fu - 1-1;//在减1是变成索引
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
for (int i = 0; i < nums.length; i++) {
|
||||
result += i <= index ? -nums[i] : nums[i];//负数直接取反,正数不管
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO 代码随想录思路:思路本质上类似,但是如果根据绝对值排序就十分的简洁
|
||||
* 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
|
||||
* 第二步:从前向后遍历,遇到负数将其变为正数,同时K--
|
||||
* 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
|
||||
* 第四步:求和
|
||||
* 速度击败12.22%,内存击败35.4% 11ms
|
||||
* @param nums
|
||||
* @param K
|
||||
* @return
|
||||
*/
|
||||
public int largestSumAfterKNegations1(int[] nums, int K) {
|
||||
// 将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
|
||||
nums = IntStream.of(nums)
|
||||
.boxed()
|
||||
.sorted((o1, o2) -> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
@ -17,7 +17,7 @@ import java.util.Arrays;
|
|||
* 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class Candy {
|
||||
public class T09_Candy {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
|
@ -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个
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue