代码随想录算法训练营第三十三天| LeetCode1005.K次取反后最大化的数组和、LeetCode134. 加油站、LeetCode135. 分发糖果

#LeetCode 1005. Maximise Sum Of Array After K Negations

#LeetCode 1005. 视频讲解:贪心算法,这不就是常识?还能叫贪心?LeetCode:1005.K次取反后最大化的数组和_哔哩哔哩_bilibili

这个题目中用到了两次局部最优,第一次是在先用k 将数组中绝对值大的负数转换为正数,这样保证了局部最优。之后又使用k 剩余的次数用在绝对值最小的数值上,这是第二次局部最优。为了提高代码的运行效率,使用k % 2 是否存在余数来快速判断,是否需要给绝对值最小的数目 * (-1) 来消耗掉k 的数值。

这里还用到了流式操作来实现对整数数组 nums 的绝对值大小排序,IntStream.of(array) 先将给定的整数数组转换为一个 IntStream 对象。 .boxed()是将 IntStream 转换为一个包装类型的 Stream,即 Stream,这样可以使用 Stream 的各种方法。 .sorted((o1, o2) -> Math.abs(o2) - Math.abs(o1)) 这对流中的元素进行排序。排序的方式是通过比较元素的绝对值大小来实现的。Comparator 接口的 lambda 表达式 (o1, o2) -> Math.abs(o2) - Math.abs(o1) 指定了排序规则,这个排序方式会将绝对值较大的元素排在前面。 .mapToInt(Integer::intValue) 这将 Stream 转换为一个 IntStream,将流中的 Integer 元素转换为其对应的基本类型 int。 .toArray()最后将排序后的 IntStream 转换回一个整数数组,则排序完成。

贪心算法代码:

class Solution {
    public int largestSumAfterKNegations(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++) {
            if (nums[i] < 0 && k > 0) {
                nums[i] *= -1;
                k--;
            }
        }
        if (k % 2 == 1) {
            nums[len - 1] *= -1;
        }
        return Arrays.stream(nums).sum();
    }
}

#LeetCode 134. Gas Station

#LeetCode 134. 视频讲解:贪心算法,得这么加油才能跑完全程!LeetCode :134.加油站_哔哩哔哩_bilibili

贪心算法的一个要点是:找到最优方案,也就是找到贪心的考虑角度。这个题目是考虑全局最优。如果是增加的油量大于消耗的油量,那么这是优化的方向。设置一个currentSum 这个参数记录从startIndex 到目前的位置中,净增的油量,如果currentSum 变为了负数,则说明已经无法到达目前的位置,那么下次就考虑从这个位置后一个数组开始继续考虑,这个位置之前的所有位置都不会再考虑。

贪心算法代码:

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int currentSum = 0;
        int totalSum = 0;
        int startIndex = 0;
        for (int i = 0; i < gas.length; i++) {
            currentSum += (gas[i] - cost[i]);
            totalSum += (gas[i] - cost[i]);
            if (currentSum < 0) {
                startIndex = i + 1;
                currentSum = 0;
            }
        }
        if (totalSum < 0) {
            return -1;
        } else {
            return startIndex;
        }
    }
}

#LeetCode 135. Candy

#LeetCode 135. 视频讲解:贪心算法,两者兼顾很容易顾此失彼!LeetCode:135.分发糖果_哔哩哔哩_bilibili

需要比较两边的情况,需要分为左右两种情况来考虑。

在第一种情况中:是左边的评分高于右侧评分,从左向后遍历,此时的局部最优是只要右边评分比左边评分高,右边的孩子就多一个糖果。

第二种情况是:左孩子大于右孩子的情况需要考虑从右往左的遍历顺序。在这里需要再次考虑贪心,从左向后遍历和从右向左遍历的结果进行比较,选择最大的糖果数目。

贪心算法代码:

class Solution {
    public int candy(int[] ratings) {
        int[] candy = new int[ratings.length];
        for (int i = 0; i < ratings.length; i++) {
            candy[i] = 1;
        }
        for (int i = 1; i < ratings.length; i++) {
            if (ratings[i] > ratings[i - 1]) {
                candy[i] = candy[i - 1] + 1;
            }
        }
        for (int j = ratings.length - 2; j >= 0; j--) {
            if (ratings[j] > ratings[j + 1]) {
                candy[j] = Math.max(candy[j], candy[j + 1] + 1);
            }
        }
        int sum = 0;
        for (int k = 0; k < ratings.length; k++) {
            sum += candy[k];
        }
        return sum;
    }
}

优化贪心算法:

class Solution {
    public int candy(int[] ratings) {
        int[] candy = new int[ratings.length];
        candy[0] = 1;
        for (int i = 1; i < ratings.length; i++) {
            candy[i] = (ratings[i] > ratings[i - 1]) ? candy[i - 1] + 1 : 1;
        }
        for (int i = ratings.length - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                candy[i] = Math.max(candy[i], candy[i + 1] + 1);
            }
        }
        int sum = 0;
        for (int num : candy) {
            sum += num;
        }
        return sum;
    }
}

你可能感兴趣的:(算法)