4.9 贪心
This commit is contained in:
parent
d28142efa1
commit
60c5f6969f
78
src/main/java/greedy/CanCompleteCircuit.java
Normal file
78
src/main/java/greedy/CanCompleteCircuit.java
Normal file
@ -0,0 +1,78 @@
|
||||
package greedy;
|
||||
/**
|
||||
* 题目: 134. 加油站 (canCompleteCircuit)
|
||||
* 描述:在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
|
||||
* 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
|
||||
* 给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
|
||||
*
|
||||
* 示例 1:
|
||||
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
|
||||
输出: 3
|
||||
解释:
|
||||
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
|
||||
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
|
||||
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
|
||||
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
|
||||
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
|
||||
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
|
||||
因此,3 可为起始索引。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/gas-station/
|
||||
*/
|
||||
public class CanCompleteCircuit {
|
||||
public int canCompleteCircuit1(int[] gas, int[] cost) {
|
||||
int sumgas=0,sumcost=0;
|
||||
int[] diff=new int[gas.length];
|
||||
for (int i = 0; i < gas.length; i++) {
|
||||
sumgas+=gas[i];
|
||||
sumcost+=cost[i];
|
||||
diff[i]=gas[i]-cost[i];
|
||||
}
|
||||
if(sumcost>sumgas)
|
||||
return -1;
|
||||
for (int i = 0; i < diff.length; i++) {
|
||||
if(diff[i]>=0){
|
||||
int j=(i+1)%diff.length;
|
||||
int cur=diff[i];
|
||||
boolean flag=true;
|
||||
while(j!=i){
|
||||
cur+=diff[j];
|
||||
if(cur<0) {
|
||||
flag=false;
|
||||
break;
|
||||
}
|
||||
j=(j+1)%diff.length;
|
||||
}
|
||||
if(flag)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
public int canCompleteCircuit(int[] gas, int[] cost) {
|
||||
int total = 0; // 总盈余:记录所有加油站油量差值的总和
|
||||
int tank = 0; // 当前油箱剩余:当前起点下的油量累计
|
||||
int start = 0; // 起始加油站索引
|
||||
|
||||
// 遍历所有加油站
|
||||
for (int i = 0; i < gas.length; i++) {
|
||||
// 当前加油站的油量差值:加油量 - 行驶消耗
|
||||
int diff = gas[i] - cost[i];
|
||||
// 累计总盈余
|
||||
total += diff;
|
||||
// 当前油箱剩余更新
|
||||
tank += diff;
|
||||
|
||||
// 如果在当前起点,从起点到 i 的累计油量不足以到达 i+1,
|
||||
// 那么必然不能从该起点出发完成环路,所以把起点更新为 i+1,
|
||||
// 并且重置当前油箱剩余为 0
|
||||
if (tank < 0) {
|
||||
start = i + 1;
|
||||
tank = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果总盈余小于 0,说明全局油量不足,返回 -1;否则返回更新后的起点
|
||||
return total < 0 ? -1 : start;
|
||||
}
|
||||
}
|
58
src/main/java/greedy/CanJump.java
Normal file
58
src/main/java/greedy/CanJump.java
Normal file
@ -0,0 +1,58 @@
|
||||
package greedy;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 题目: 55. 跳跃游戏 (canJump)
|
||||
* 描述:给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
|
||||
* 判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
|
||||
|
||||
* 示例 1:
|
||||
输入:nums = [2,3,1,1,4]
|
||||
输出:true
|
||||
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/jump-game/
|
||||
*/
|
||||
public class CanJump {
|
||||
//接近超时!记忆化搜索,记录每一个状态(这里是索引 i)是否能成功跳到最后。
|
||||
public boolean canJump1(int[] nums) {
|
||||
// -1 表示未计算,0 表示不能到达,1 表示能到达
|
||||
int[] memo = new int[nums.length];
|
||||
Arrays.fill(memo, -1);
|
||||
return helper(nums, 0, memo);
|
||||
}
|
||||
|
||||
boolean helper(int[] nums, int i, int[] memo) {
|
||||
if (i >= nums.length - 1) return true;
|
||||
if (memo[i] != -1) return memo[i] == 1; //-1代表没访问过,0代表失败,1代表成功
|
||||
|
||||
int max = nums[i];
|
||||
for (int j = 1; j <= max; j++) {
|
||||
if (helper(nums, i + j, memo)) {
|
||||
memo[i] = 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
memo[i] = 0; //尝试了所有从当前位置 i 开始能跳的范围后,都没有跳到终点,说明从 i 出发是失败的。
|
||||
return false;
|
||||
}
|
||||
//太妙了!
|
||||
public boolean canJump(int[] nums) {
|
||||
int maxReach = 0; // 表示当前能到达的最远位置
|
||||
for (int i = 0; i < nums.length; i++) {
|
||||
// 如果当前位置超过了当前能到达的最远位置,则无法到达该位置,返回 false
|
||||
if (i > maxReach) {
|
||||
return false;
|
||||
}
|
||||
// 更新当前能到达的最远位置
|
||||
maxReach = Math.max(maxReach, i + nums[i]);
|
||||
// 如果能到达最后一个位置,则返回 true
|
||||
if (maxReach >= nums.length - 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
51
src/main/java/greedy/Candy.java
Normal file
51
src/main/java/greedy/Candy.java
Normal file
@ -0,0 +1,51 @@
|
||||
package greedy;
|
||||
/**
|
||||
* 题目: 135. 分发糖果 (candy)
|
||||
* 描述:n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
|
||||
* 你需要按照以下要求,给这些孩子分发糖果:
|
||||
* 每个孩子至少分配到 1 个糖果。
|
||||
* 相邻两个孩子评分更高的孩子会获得更多的糖果。
|
||||
* 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
|
||||
|
||||
示例 2:
|
||||
输入:ratings = [1,2,2]
|
||||
输出:4
|
||||
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
|
||||
第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/candy/
|
||||
*/
|
||||
//没想到,想复杂了
|
||||
public class Candy {
|
||||
public int candy(int[] ratings) {
|
||||
int n = ratings.length;
|
||||
int[] candies = new int[n];
|
||||
|
||||
// 第一步:初始化每个孩子至少1颗糖果
|
||||
for (int i = 0; i < n; i++) {
|
||||
candies[i] = 1;
|
||||
}
|
||||
|
||||
// 第二步:从左到右遍历,如果右侧评分高,给右侧多1颗
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (ratings[i] > ratings[i - 1]) {
|
||||
candies[i] = candies[i - 1] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 第三步:从右到左遍历,如果左侧评分高,确保左侧孩子糖果更多
|
||||
for (int i = n - 2; i >= 0; i--) {
|
||||
if (ratings[i] > ratings[i + 1]) {
|
||||
candies[i] = Math.max(candies[i], candies[i + 1] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 求出总糖果数
|
||||
int sum = 0;
|
||||
for (int candy : candies) {
|
||||
sum += candy;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
}
|
34
src/main/java/greedy/FindMinArrowShots.java
Normal file
34
src/main/java/greedy/FindMinArrowShots.java
Normal file
@ -0,0 +1,34 @@
|
||||
package greedy;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 题目: 452. 用最少数量的箭引爆气球 (findMinArrowShots)
|
||||
* 描述:有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
|
||||
* 一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
|
||||
* 给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。
|
||||
|
||||
示例 2:
|
||||
输入:points = [[10,16],[2,8],[1,6],[7,12]]
|
||||
输出:2
|
||||
解释:气球可以用2支箭来爆破:
|
||||
-在x = 6处射出箭,击破气球[2,8]和[1,6]。
|
||||
-在x = 11处发射箭,击破气球[10,16]和[7,12]。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/
|
||||
*/
|
||||
public class FindMinArrowShots {
|
||||
public int findMinArrowShots(int[][] points) {
|
||||
Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1]));
|
||||
|
||||
int index=0,cnt=0;
|
||||
while(index<points.length){
|
||||
int temp=index;
|
||||
while(index<points.length&&points[temp][1]>=points[index][0])
|
||||
index++;
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
}
|
49
src/main/java/greedy/Jump.java
Normal file
49
src/main/java/greedy/Jump.java
Normal file
@ -0,0 +1,49 @@
|
||||
package greedy;
|
||||
|
||||
/**
|
||||
* 题目: 45. 跳跃游戏 II (jump)
|
||||
* 描述:给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
|
||||
* 每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
|
||||
* 0 <= j <= nums[i]
|
||||
* i + j < n
|
||||
* 返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。
|
||||
|
||||
* 示例 1:
|
||||
输入: nums = [2,3,1,1,4]
|
||||
输出: 2
|
||||
解释: 跳到最后一个位置的最小跳跃数是 2。
|
||||
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/jump-game-ii/
|
||||
*/
|
||||
|
||||
//不会 https://programmercarl.com/0045.%E8%B7%B3%E8%B7%83%E6%B8%B8%E6%88%8FII.html#%E6%80%9D%E8%B7%AF
|
||||
public class Jump {
|
||||
//要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最少步数!
|
||||
public int jump(int[] nums) {
|
||||
if (nums == null || nums.length == 0 || nums.length == 1) {
|
||||
return 0;
|
||||
}
|
||||
//记录跳跃的次数
|
||||
int count=0;
|
||||
//当前的覆盖最大区域
|
||||
int curDistance = 0;
|
||||
//最大的覆盖区域
|
||||
int maxDistance = 0;
|
||||
for (int i = 0; i < nums.length; i++) {
|
||||
//在可覆盖区域内更新最大的覆盖区域
|
||||
maxDistance = Math.max(maxDistance,i+nums[i]);
|
||||
//说明当前一步,再跳一步就到达了末尾
|
||||
if (maxDistance>=nums.length-1){
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
//遍历到当前覆盖的最大区域时,更新下一步可达的最大区域
|
||||
if (i==curDistance){
|
||||
curDistance = maxDistance;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
48
src/main/java/greedy/LargestSumAfterKNegations.java
Normal file
48
src/main/java/greedy/LargestSumAfterKNegations.java
Normal file
@ -0,0 +1,48 @@
|
||||
package greedy;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 题目: 1005. K 次取反后最大化的数组和 (largestSumAfterKNegations)
|
||||
* 描述:给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:
|
||||
* 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。
|
||||
* 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。
|
||||
* 以这种方式修改数组后,返回数组 可能的最大和 。
|
||||
*
|
||||
* 示例 1:
|
||||
输入:nums = [4,2,3], k = 1
|
||||
输出:5
|
||||
解释:选择下标 1 ,nums 变为 [4,-2,3] 。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/maximize-sum-of-array-after-k-negations/
|
||||
*/
|
||||
//有点绕
|
||||
public class LargestSumAfterKNegations {
|
||||
//让绝对值大的负数变为正数,当前数值达到最大 那么如果将负数都转变为正数了,K依然大于0 只找数值最小的正整数进行反转
|
||||
public int largestSumAfterKNegations(int[] nums, int k) {
|
||||
if (nums.length == 1) return nums[0];
|
||||
|
||||
// 排序:先把负数处理了
|
||||
Arrays.sort(nums);
|
||||
|
||||
for (int i = 0; i < nums.length && k > 0; i++) { // 贪心点, 通过负转正, 消耗尽可能多的k
|
||||
if (nums[i] < 0) {
|
||||
nums[i] = -nums[i];
|
||||
k--;
|
||||
}
|
||||
}
|
||||
|
||||
// 退出循环, k > 0 || k < 0 (k消耗完了不用讨论)
|
||||
if (k % 2 == 1) { // k > 0 && k is odd:对于负数:负-正-负-正
|
||||
Arrays.sort(nums); // 再次排序得到最小的正数
|
||||
nums[0] = -nums[0];
|
||||
}
|
||||
// k > 0 && k is even,flip数字不会产生影响: 对于负数: 负-正-负;对于正数:正-负-正
|
||||
|
||||
int sum = 0;
|
||||
for (int num : nums) { // 计算最大和
|
||||
sum += num;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
68
src/main/java/greedy/LemonadeChange.java
Normal file
68
src/main/java/greedy/LemonadeChange.java
Normal file
@ -0,0 +1,68 @@
|
||||
package greedy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 题目: 860. 柠檬水找零 (lemonadeChange)
|
||||
* 描述:在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
|
||||
* 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
|
||||
* 注意,一开始你手头没有任何零钱。
|
||||
* 给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
|
||||
*
|
||||
* 示例 1:
|
||||
输入:bills = [5,5,5,10,20]
|
||||
输出:true
|
||||
解释:
|
||||
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
|
||||
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
|
||||
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
|
||||
由于所有客户都得到了正确的找零,所以我们输出 true。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/lemonade-change/
|
||||
*/
|
||||
public class LemonadeChange {
|
||||
public boolean lemonadeChange(int[] bills) {
|
||||
int five = 0; // 记录 5 美元的数量
|
||||
int ten = 0; // 记录 10 美元的数量
|
||||
|
||||
// 遍历每一笔账单
|
||||
for (int bill : bills) {
|
||||
switch (bill) {
|
||||
case 5:
|
||||
// 收到 5 美元,不需找零
|
||||
five++;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
// 收到 10 美元,需要找回 5 美元
|
||||
if (five > 0) {
|
||||
five--; // 找回一张 5 美元
|
||||
ten++; // 保存这张 10 美元
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 20:
|
||||
// 收到 20 美元,需要找回 15 美元
|
||||
// 优先使用一张 10 美元和一张 5 美元找零
|
||||
if (ten > 0 && five > 0) {
|
||||
ten--;
|
||||
five--;
|
||||
}
|
||||
// 如果没有 10 美元,则用三张 5 美元找零
|
||||
else if (five >= 3) {
|
||||
five -= 3;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,9 +1,5 @@
|
||||
package greedy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 题目: 121. 买卖股票的最佳时机 (maxProfit)
|
||||
* 描述:给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
|
||||
@ -18,8 +14,7 @@ import java.util.List;
|
||||
|
||||
* 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
|
||||
*/
|
||||
//第一道贪心,学思路
|
||||
//每次都假设是今天卖出,然后求今天之前的历史最低点。
|
||||
//第一次做没想出来,每次都假设是今天卖出,然后求今天之前的历史最低点。
|
||||
public class MaxProfit {
|
||||
public int maxProfit(int prices[]) {
|
||||
int minprice = Integer.MAX_VALUE;
|
||||
@ -27,7 +22,7 @@ public class MaxProfit {
|
||||
for (int price : prices) {
|
||||
if (price < minprice) {
|
||||
minprice = price;
|
||||
} else if (price - minprice > maxprofit) {
|
||||
} else if (price - minprice > maxprofit) { //else if 是因为今日如果买进,今日卖出收益为0
|
||||
maxprofit = price - minprice;
|
||||
}
|
||||
}
|
||||
|
49
src/main/java/greedy/MaxProfit2.java
Normal file
49
src/main/java/greedy/MaxProfit2.java
Normal file
@ -0,0 +1,49 @@
|
||||
package greedy;
|
||||
/**
|
||||
* 题目: 122. 买卖股票的最佳时机 II (maxProfit)
|
||||
* 描述:给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
|
||||
* 在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
|
||||
* 返回 你能获得的 最大 利润 。
|
||||
|
||||
* 示例 1:
|
||||
输入:prices = [7,1,5,3,6,4]
|
||||
输出:7
|
||||
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
|
||||
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3。
|
||||
最大总利润为 4 + 3 = 7 。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/
|
||||
*/
|
||||
//考虑复杂了!
|
||||
public class MaxProfit2 {
|
||||
public int maxProfit1(int[] prices) {
|
||||
int prediff=0,curdiff=0,maxprofit=0,buyprice=0;
|
||||
boolean flag=false;
|
||||
for (int i = 1; i < prices.length; i++) {
|
||||
curdiff=prices[i]-prices[i-1];
|
||||
if(!flag){
|
||||
if(prediff<=0 &&curdiff>0){
|
||||
flag=true;
|
||||
buyprice=prices[i-1];
|
||||
}
|
||||
}else {
|
||||
if(prediff>=0&&curdiff<0){
|
||||
flag=false;
|
||||
maxprofit+=prices[i-1]-buyprice;
|
||||
}
|
||||
}
|
||||
prediff=curdiff;
|
||||
}
|
||||
if(flag)
|
||||
maxprofit+=prices[prices.length-1]-buyprice;
|
||||
return maxprofit;
|
||||
}
|
||||
//收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间。
|
||||
public int maxProfit(int[] prices) {
|
||||
int result = 0;
|
||||
for (int i = 1; i < prices.length; i++) {
|
||||
result += Math.max(prices[i] - prices[i - 1], 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
88
src/main/java/greedy/PartitionLabels.java
Normal file
88
src/main/java/greedy/PartitionLabels.java
Normal file
@ -0,0 +1,88 @@
|
||||
package greedy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 题目: 763. 划分字母区间 (partitionLabels)
|
||||
* 描述:给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。例如,字符串 "ababcc" 能够被分为 ["abab", "cc"],但类似 ["aba", "bcc"] 或 ["ab", "ab", "cc"] 的划分是非法的。
|
||||
* 注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。
|
||||
* 返回一个表示每个字符串片段的长度的列表。
|
||||
|
||||
示例 2:
|
||||
输入:s = "ababcbacadefegdehijhklij"
|
||||
输出:[9,7,8]
|
||||
解释:
|
||||
划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
|
||||
每个字母最多出现在一个片段中。
|
||||
像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/partition-labels/
|
||||
*/
|
||||
public class PartitionLabels {
|
||||
/**
|
||||
* 可以通过,但是时间复杂度高!
|
||||
预先计算字符最后出现位置
|
||||
一种常见且高效的做法是在遍历字符串前,用一个 Map<Character, Integer> 或者数组记录下每个字符的最后出现索引。这样,在后续遍历时,能够通过字符直接查到其最后出现位置,避免重复扫描。
|
||||
|
||||
维护当前区间右边界
|
||||
在遍历过程中,维护一个 end 变量表示当前区间内所有字符最后出现位置的最大值。每遇到一个新的字符,就更新 end = Math.max(end, lastIndex[当前字符])。当遍历索引等于 end 时,说明当前区间已经完整,可以结束分区并记录长度。
|
||||
|
||||
简化逻辑避免重复循环
|
||||
采用上述方法后,代码只需要一次遍历即可确定所有分区长度,无需额外遍历区间内所有字符进行检查。
|
||||
*/
|
||||
public List<Integer> partitionLabels1(String s) {
|
||||
HashMap<Character,Integer>map=new HashMap<>();
|
||||
List<Integer>list=new ArrayList<>();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
map.put(s.charAt(i),map.getOrDefault(s.charAt(i),0)+1);
|
||||
}
|
||||
StringBuffer buffer=new StringBuffer();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
boolean flag=true;
|
||||
char cur=s.charAt(i);
|
||||
buffer.append(cur);
|
||||
int newCount = map.get(cur) - 1;
|
||||
if (newCount > 0) {
|
||||
map.put(cur, newCount);
|
||||
} else {
|
||||
map.remove(cur);
|
||||
}
|
||||
for (int j = 0; j < buffer.length(); j++) {
|
||||
if (map.containsKey(buffer.charAt(j))) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(flag) {
|
||||
list.add(buffer.length());
|
||||
buffer.setLength(0);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
//看这个
|
||||
public List<Integer> partitionLabels(String s) {
|
||||
// 用于记录每个字符最后一次出现的索引
|
||||
HashMap<Character, Integer> lastOccurrence = new HashMap<>();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
lastOccurrence.put(s.charAt(i), i);
|
||||
}
|
||||
|
||||
List<Integer> partitionSizes = new ArrayList<>();
|
||||
int start = 0, end = 0; // start 记录当前分区开始的位置
|
||||
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
// 更新当前分区的最远截止位置
|
||||
end = Math.max(end, lastOccurrence.get(s.charAt(i)));
|
||||
// 当遍历到分区的最远截止位置时,说明可以划分一个区间
|
||||
if (i == end) {
|
||||
partitionSizes.add(end - start + 1);
|
||||
start = i + 1; // 下一分区的起始位置
|
||||
}
|
||||
}
|
||||
|
||||
return partitionSizes;
|
||||
}
|
||||
}
|
41
src/main/java/greedy/ReconstructQueue.java
Normal file
41
src/main/java/greedy/ReconstructQueue.java
Normal file
@ -0,0 +1,41 @@
|
||||
package greedy;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* 题目: 406. 根据身高重建队列 II (maxProfit)
|
||||
* 描述:假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
|
||||
* 请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
|
||||
|
||||
* 示例 1:
|
||||
输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
|
||||
输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
|
||||
解释:
|
||||
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
|
||||
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
|
||||
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
|
||||
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
|
||||
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
|
||||
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
|
||||
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。
|
||||
|
||||
* 链接:https://leetcode.cn/problems/queue-reconstruction-by-height/
|
||||
*/
|
||||
//不会 ,很妙
|
||||
public class ReconstructQueue {
|
||||
public int[][] reconstructQueue(int[][] people) {
|
||||
// 身高从大到小排(身高相同k小的站前面)
|
||||
Arrays.sort(people, (a, b) -> {
|
||||
if (a[0] == b[0]) return a[1] - b[1]; // a - b 是升序排列,故在a[0] == b[0]的狀況下,會根據k值升序排列
|
||||
return b[0] - a[0]; //b - a 是降序排列,在a[0] != b[0],的狀況會根據h值降序排列
|
||||
});
|
||||
|
||||
LinkedList<int[]> que = new LinkedList<>();
|
||||
for (int[] p : people) {
|
||||
que.add(p[1],p); //Linkedlist.add(index, value),會將value插入到指定index裡。
|
||||
}
|
||||
|
||||
return que.toArray(new int[people.length][]);
|
||||
}
|
||||
}
|
16
src/test/java/greedy/CanCompleteCircuitTest.java
Normal file
16
src/test/java/greedy/CanCompleteCircuitTest.java
Normal file
@ -0,0 +1,16 @@
|
||||
package greedy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class CanCompleteCircuitTest {
|
||||
|
||||
@Test
|
||||
public void canCompleteCircuit() {
|
||||
int[] gas = {1,2,3,4,5}, cost = {3,4,5,1,2};
|
||||
CanCompleteCircuit solution = new CanCompleteCircuit();
|
||||
int res=solution.canCompleteCircuit(gas,cost);
|
||||
System.out.println(res);
|
||||
}
|
||||
}
|
18
src/test/java/greedy/FindMinArrowShotsTest.java
Normal file
18
src/test/java/greedy/FindMinArrowShotsTest.java
Normal file
@ -0,0 +1,18 @@
|
||||
package greedy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class FindMinArrowShotsTest {
|
||||
|
||||
@Test
|
||||
public void findMinArrowShots() {
|
||||
// int[][] points = {{10,16},{2,8},{1,6},{7,12}};
|
||||
// int[][] points ={{1,2},{3,4},{4,5},{2,3}};
|
||||
int[][] points = {{-2147483646,-2147483645},{2147483646,2147483647}};
|
||||
FindMinArrowShots solution = new FindMinArrowShots();
|
||||
int res=solution.findMinArrowShots(points);
|
||||
System.out.println(res);
|
||||
}
|
||||
}
|
16
src/test/java/greedy/JumpTest.java
Normal file
16
src/test/java/greedy/JumpTest.java
Normal file
@ -0,0 +1,16 @@
|
||||
package greedy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class JumpTest {
|
||||
|
||||
@Test
|
||||
public void jump() {
|
||||
int[] nums = new int[]{2,2,1,2,4};
|
||||
Jump solution = new Jump();
|
||||
int res=solution.jump(nums);
|
||||
System.out.println(res);
|
||||
}
|
||||
}
|
16
src/test/java/greedy/MaxProfit2Test.java
Normal file
16
src/test/java/greedy/MaxProfit2Test.java
Normal file
@ -0,0 +1,16 @@
|
||||
package greedy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class MaxProfit2Test {
|
||||
|
||||
@Test
|
||||
public void maxProfit() {
|
||||
int[]prices = {1,2,3,4,5};
|
||||
MaxProfit2 solution = new MaxProfit2();
|
||||
int res=solution.maxProfit(prices);
|
||||
System.out.println(res);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user