leecode更新
This commit is contained in:
parent
e18305c54a
commit
ba62bbc0a2
|
|
@ -18,7 +18,7 @@ public class CanCompleteCircuit {
|
||||||
@Test
|
@Test
|
||||||
public void test() {
|
public void test() {
|
||||||
int[] gas = {1, 2, 3, 4, 5}, cost = {3, 4, 5, 1, 2};
|
int[] gas = {1, 2, 3, 4, 5}, cost = {3, 4, 5, 1, 2};
|
||||||
System.out.println(canCompleteCircuit(gas, cost));
|
System.out.println(canCompleteCircuit1(gas, cost));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,4 +72,114 @@ public class CanCompleteCircuit {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 官方代码思路:我们首先检查第 0个加油站,并试图判断能否环绕一周;如果不能,就从第一个无法到达的加油站开始继续检查。
|
||||||
|
* 速度击败78.24%,内存击败16.6%
|
||||||
|
* 这种思路理论上来说是O(N),但是实际上可能是O(N^2),因为他遍历以后还需要至少重头开始计算一次,
|
||||||
|
* 但是实际上可以通过数学推导进行避免,例如本人的思路,当然本人的思路的前提是题目只有唯一解,如果有多个解本人方法是没办法找的,题解方法可以
|
||||||
|
* @param gas
|
||||||
|
* @param cost
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int canCompleteCircuit1(int[] gas, int[] cost) {
|
||||||
|
int n = gas.length;
|
||||||
|
int i = 0;
|
||||||
|
while (i < n) {
|
||||||
|
int sumOfGas = 0, sumOfCost = 0;
|
||||||
|
int cnt = 0;
|
||||||
|
while (cnt < n) {
|
||||||
|
//使得一定会转一圈回到原地
|
||||||
|
int j = (i + cnt) % n;
|
||||||
|
sumOfGas += gas[j];
|
||||||
|
sumOfCost += cost[j];
|
||||||
|
if (sumOfCost > sumOfGas) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
//判断是否是转了一圈,如果转了一圈,则证明找到了那个唯一解
|
||||||
|
if (cnt == n) {
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
//没有转一圈,从头开始找
|
||||||
|
i = i + cnt + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码随想录贪心算法一思路:
|
||||||
|
* 情况一:gas总和小于cost,永远到不了
|
||||||
|
* 情况二:rest=gas-cost的累加和一直大于0,则从0出发
|
||||||
|
* 情况二:rest=gas-cost小于0了,则没办法到下一站,则直接从下一站非0点出发
|
||||||
|
* @param gas
|
||||||
|
* @param cost
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int canCompleteCircuit2(int[] gas, int[] cost) {
|
||||||
|
int curSum=0;
|
||||||
|
int min=Integer.MAX_VALUE; //从起点出发,油箱里的油量的最小值
|
||||||
|
|
||||||
|
for (int i = 0; i < gas.length; i++) {
|
||||||
|
int rest=gas[i]-cost[i];
|
||||||
|
curSum+=rest;
|
||||||
|
if(curSum<min){
|
||||||
|
min=curSum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(curSum<0)return -1; //情况一
|
||||||
|
if(min>=0)return 0; //情况二
|
||||||
|
|
||||||
|
//情况三
|
||||||
|
//这里开始反向加,如果加完之后大于0了,则证明一定可以转一圈,反之如果怎么加都不能加到则-1
|
||||||
|
for (int i =gas.length-1; i > 0; i--) {
|
||||||
|
int rest=gas[i]-cost[i];
|
||||||
|
min+=rest;
|
||||||
|
|
||||||
|
if(min>=0){
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码随想录贪心算法二思路:
|
||||||
|
* 与本人方法类似,不需要使用flag
|
||||||
|
* 速度击败78.24%,内存击败35.3%
|
||||||
|
* @param gas
|
||||||
|
* @param cost
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int canCompleteCircuit3(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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
package com.markilue.leecode.greedy;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: Leecode
|
||||||
|
* @BelongsPackage: com.markilue.leecode.greedy
|
||||||
|
* @Author: markilue
|
||||||
|
* @CreateTime: 2022-11-09 11:19
|
||||||
|
* @Description: TODO 力扣135题 分发糖果:
|
||||||
|
* n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
|
||||||
|
* 你需要按照以下要求,给这些孩子分发糖果:
|
||||||
|
* 每个孩子至少分配到 1 个糖果。
|
||||||
|
* 相邻两个孩子评分更高的孩子会获得更多的糖果。
|
||||||
|
* 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
|
||||||
|
* @Version: 1.0
|
||||||
|
*/
|
||||||
|
public class Candy {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
int[] ratings = {1, 0, 2};
|
||||||
|
System.out.println(candy(ratings));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test1() {
|
||||||
|
int[] ratings = {1, 2, 2};
|
||||||
|
System.out.println(candy(ratings));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test2() {
|
||||||
|
int[] ratings = {5, 4, 3, 2, 1, 2, 6, 4, 3, 2, 1};
|
||||||
|
System.out.println(candy2(ratings));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test3() {
|
||||||
|
int[] ratings = {1, 2, 3, 4, 5, 3, 1, 2, 3, 5};
|
||||||
|
System.out.println(candy(ratings));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test4() {
|
||||||
|
int[] ratings = {1, 3, 2, 2, 1};
|
||||||
|
System.out.println(candy(ratings)); //7
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test5() {
|
||||||
|
int[] ratings = {29, 51, 87, 87, 72, 12};
|
||||||
|
System.out.println(candy2(ratings)); //12
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本人思路:要计算当前位置至少需要多少糖果,如果观察局部左右的最小值的位置,
|
||||||
|
* 因此需要寻找一个单调递减的位置的。然后candy依次递增累加
|
||||||
|
* todo 尚且有问题,对于test5不行,问题在于:对于相等值的边界处理,现在是如果相等就跟之前反向,则test5过不去
|
||||||
|
*
|
||||||
|
* @param ratings
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int candy(int[] ratings) {
|
||||||
|
|
||||||
|
int sum = 0;
|
||||||
|
int countMin = 1; //记录单调下降区间中的count数
|
||||||
|
int countMax = 1; //记录单调上升区间中的count数
|
||||||
|
int pre = 1;
|
||||||
|
boolean flag = true;
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < ratings.length - 1; i++) {
|
||||||
|
|
||||||
|
int cur = ratings[i + 1] - ratings[i];
|
||||||
|
if (cur == 0 && i > 0) {
|
||||||
|
cur = -(ratings[i] - ratings[i - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur < 0) {
|
||||||
|
if (!flag) {
|
||||||
|
pre = countMax;
|
||||||
|
countMax = 1;
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
//单调递减区间
|
||||||
|
sum += countMin;
|
||||||
|
countMin = countMin + 1;
|
||||||
|
} else {
|
||||||
|
//单调递增区间
|
||||||
|
//找到局部最小值的转折点了
|
||||||
|
//对于极值点的判断
|
||||||
|
if (flag) {
|
||||||
|
if (countMin > pre) {
|
||||||
|
sum += countMin;
|
||||||
|
} else {
|
||||||
|
sum += pre;
|
||||||
|
}
|
||||||
|
countMin = 1;
|
||||||
|
countMax = 2;
|
||||||
|
flag = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += countMax;
|
||||||
|
countMax = countMax + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
if (countMax == 1) {
|
||||||
|
if (countMin > pre) {
|
||||||
|
sum += countMin;
|
||||||
|
} else {
|
||||||
|
sum += pre;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sum += countMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码随想录思路:对于贪心策略的使用,如果在考虑局部最优的时候想兼顾两边,就会顾此失彼
|
||||||
|
* 本题则需要采用两次贪心的策略:
|
||||||
|
* 一次是从左到右的遍历,只比较右边孩子的评分比左边大的情况
|
||||||
|
* 另一次是从右到左遍历,只比较左边孩子的评分比右边大的情况
|
||||||
|
* 速度击败98.66%,内存击败6.19%
|
||||||
|
* @param ratings
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int candy1(int[] ratings) {
|
||||||
|
|
||||||
|
int[] candyVec = new int[ratings.length];
|
||||||
|
Arrays.fill(candyVec, 1);
|
||||||
|
|
||||||
|
//从前往后遍历
|
||||||
|
for (int i = 1; i < ratings.length; i++) {
|
||||||
|
if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;
|
||||||
|
}
|
||||||
|
//从后前遍历
|
||||||
|
for (int i = ratings.length - 2; i >= 0; i--) {
|
||||||
|
//这里是判断从左往右的需求大还是从右往左的需要大
|
||||||
|
if (ratings[i] > ratings[i + 1]) candyVec[i] = Math.max(candyVec[i], candyVec[i + 1] + 1);
|
||||||
|
}
|
||||||
|
//统计结果
|
||||||
|
int result=0;
|
||||||
|
for (int i = 0; i < candyVec.length; i++) {
|
||||||
|
result+=candyVec[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 官方常数空间思路:
|
||||||
|
* 从左到右枚举每一个同学,记前一个同学分得的糖果数量为 pre:
|
||||||
|
* 如果当前同学比上一个同学评分高,说明我们就在最近的递增序列中,直接分配给该同学 pre+1个糖果即可。
|
||||||
|
* 否则我们就在一个递减序列中,我们直接分配给当前同学一个糖果,并把该同学所在的递减序列中所有的同学都再多分配一个糖果,以保证糖果数量还是满足条件。
|
||||||
|
* 我们无需显式地额外分配糖果,只需要记录当前的递减序列长度,即可知道需要额外分配的糖果数量。
|
||||||
|
* 同时注意当当前的递减序列长度和上一个递增序列等长时,需要把最近的递增序列的最后一个同学也并进递减序列中。
|
||||||
|
* 这样,我们只要记录当前递减序列的长度dec,最近的递增序列的长度 inc 和前一个同学分得的糖果数量 pre 即可。
|
||||||
|
* 速度击败98.66%,内存击败34.77%
|
||||||
|
* @param ratings
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int candy2(int[] ratings) {
|
||||||
|
|
||||||
|
int n = ratings.length;
|
||||||
|
int ret = 1;
|
||||||
|
//inc记录上升时,上次的糖果数;dec记录下降时的糖果数
|
||||||
|
int inc = 1, dec = 0, pre = 1;
|
||||||
|
for (int i = 1; i < n; i++) {
|
||||||
|
if (ratings[i] >= ratings[i - 1]) {
|
||||||
|
dec = 0;
|
||||||
|
pre = ratings[i] == ratings[i - 1] ? 1 : pre + 1;
|
||||||
|
ret += pre;
|
||||||
|
inc = pre;
|
||||||
|
} else {
|
||||||
|
dec++;
|
||||||
|
//这里是最关键的一点,比较神奇,核心在于dec=inc则左右一样长了,那么最上面的节点也需要加1,
|
||||||
|
// 这里dec+1,相当于帮助上边的节点完成+1操作;而往后每多一个都需要帮助最上面的节点+1,而当dec+1以后相当于往后每一个都多帮最上边的节点+1,
|
||||||
|
// 从而巧妙的只需要一次遍历即可完成操作
|
||||||
|
if (dec == inc) {
|
||||||
|
dec++;
|
||||||
|
}
|
||||||
|
ret += dec;
|
||||||
|
pre = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue