1005.K次取反后最大化的数组和
题目链接:1005. K 次取反后最大化的数组和 - 力扣(LeetCode)
解题思路:
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
int sum = 0;
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++){
if(nums[i] < 0 && k > 0){
nums[i] = -nums[i];
k--;
}
}
Arrays.sort(nums);
k = k % 2;
if(k == 1){
nums[0] = -nums[0];
}
for(int n : nums){
sum += n;
}
return sum;
}
}
这个方法是使用普通排序的方法做的,这样做有一个坏处就是,反转到已经没有负数了,但是K还不为0的情况,需要判断哪一个数才是最小的数。要么就再次进行排序,要么就需要进行复杂的逻辑判断。所以使用了Stream流来进行特殊的排序,按照绝对值的大小进行排序。这也就体现了贪心的思想。
贪心贪的就是:先反转绝对值最大的负数。
这样写可以让逻辑更清晰。
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
int sum = 0;
// 数组排序
nums = IntStream.of(nums)
.boxed()
.sorted((o1,o2) -> Math.abs(o2) - Math.abs(o1))
.mapToInt(Integer::intValue).toArray();
// 反转负数
for(int i = 0; i < nums.length; i++){
if(nums[i] < 0 && k > 0){
nums[i] = -nums[i];
k--;
}
}
k = k % 2;
if(k == 1) nums[nums.length - 1] = -nums[nums.length - 1];
for(int n : nums){
sum += n;
}
return sum;
}
}
关于Stream流的学习链接:
(117条消息) IntStream用法wang0907的博客-CSDN博客intstream
(117条消息) Java8中Stream为什么要boxed信仰273993243的博客-CSDN博客_boxed()
(117条消息) Stream流简析_Angletank的博客-CSDN博客
134.加油站
解题思路:
暴力解法,每个结点都尝试一次,只不过中间加了一些剪枝操作
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
// -2 -2 -2 3 3
// 返回
int[] record = new int[gas.length];
for(int i = 0; i < record.length; i++){
record[i] = gas[i] - cost[i];
}
if(record.length == 1){
if(record[0] < 0) return -1;
else return 0;
}
for(int i = 0; i < record.length; i++){ // 模拟每一个起点
int sum = record[i];
if(sum <= 0) continue;
int j = (i + 1) % record.length;
while(j != i){ //
sum += record[j];
if(sum < 0) break;
j = (j + 1) % record.length;
}
if(j == i) return j;
}
return -1;
}
}
优化版!前面逻辑这么复杂的原因就是没有想清楚while循环的条件,什么时候终止循环。
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
for(int i = 0; i < gas.length; i++){ // 模拟每一个起点
int record = gas[i] - cost[i]; // 用一个数记录剩余油量
int j = (i + 1) % gas.length;
while(j != i && record > 0){ // 继续循环条件!还有油并且没走一圈
record += (gas[j] - cost[j]);
j = (j + 1) % gas.length;
}
// 如果是走了一圈直接返回即可,否则什么也不做,这个起点不可行,换一个起点
if(record >= 0 && j == i) return j;
}
// 所有起点都失败,返回-1
return -1;
}
}
贪心写法:
先尝试从0开始走一遍,看看最多能缺多少油,然后从后往前找,看看到哪里才能把这个缺的油补上。
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
// 贪心1
int curSum = 0;
int min = Integer.MAX_VALUE;
// 从0出发,看看全程最小的和是多少
for(int i = 0; i < gas.length; i++){
int record = gas[i] - cost[i];
curSum += record;
if(curSum < min){
min = curSum;
}
}
if(min > 0) return 0; // 从头到尾没缺过油
if(curSum < 0) return -1; // 不管从哪走都走不到最后
// 一定能走一圈,看看起点在哪
for(int i = gas.length - 1; i >= 0; i--){
int record = gas[i] - cost[i];
min += record;
if(min >= 0) return i;
}
return -1;
}
}
贪心写法2:
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
// 贪心2
int record = 0; // 区间和,为负立马清零,更新起点
int sum = 0; // 总和
int start = 0; // 车的起点
for(int i = 0; i < gas.length; i++){
record += (gas[i] - cost[i]);
sum += (gas[i] - cost[i]);
if(record < 0){
record = 0;
start = i + 1;
}
}
if(sum < 0) return -1;
return start;
}
}
135.分发糖果
题目链接:Loading Question... - 力扣(LeetCode)
解题思路:
该题目解题一共分两步:
1.先让所有元素满足其和左侧元素的关系
2.再让所有元素满足其和右侧元素的关系
class Solution {
public int candy(int[] ratings) {
// 先从左到右,再从右到左
int[] record = new int[ratings.length];
Arrays.fill(record, 1);
// 从左到有,使所有左侧的元素相对于右侧元素满足条件。
for(int i = 1; i < ratings.length; i++){
if(ratings[i] > ratings[i - 1]){ // 右比左大
record[i] = record[i - 1] + 1;
}
}
System.out.println(Arrays.stream(record).boxed().collect(Collectors.toList()));
// 再从右向左,在一侧已经满足条件的前提下,使另一侧也满足
for(int i = ratings.length - 2; i >= 0; i--){
if(ratings[i] > ratings[i + 1]){ // 左比右大
record[i] = Math.max(record[i + 1] + 1, record[i]);
}
}
System.out.println(Arrays.stream(record).boxed().collect(Collectors.toList()));
int sum = 0;
for(int r : record){
sum += r;
}
return sum;
}
}