leetcode-zgd-day33-1005.K次取反后最大化的数组和/134.加油站/135.分发糖果

81 阅读3分钟

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.加油站

题目链接:134. 加油站 - 力扣(LeetCode)

解题思路:

暴力解法,每个结点都尝试一次,只不过中间加了一些剪枝操作

 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;
     }
 }