代码学习记录27---贪心算法

随想录日记part27

t i m e : time: time: 2024.03.25



主要内容:今天深入学习贪心算法,接下来是针对题目的讲解:1.K次取反后最大化的数组和 ;2. 加油站 ;3.分发糖果



K次取反后最大化的数组和

题目:

给你一个整数数组 n u m s nums nums 和一个整数 k k k ,按以下方法修改该数组:

  • 选择某个下标 i i i 并将 n u m s i numsi numsi 替换为 − n u m s i -numsi −numsi
  • 重复这个过程恰好 k k k 次。可以多次选择同一个下标 i i i 。

以这种方式修改数组后,返回数组 可能的最大和 。

输入: n u m s = 4 , 2 , 3 , k = 1 nums = 4,2,3, k = 1 nums=4,2,3,k=1
输出: 5 5 5

思路:

代码随想录的解题步骤为:

第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小

第二步:从前向后遍历,遇到负数将其变为正数,同时 K − − K-- K−−

第三步:如果 K K K还大于0,那么反复转变数值最小的元素,将 K K K用完

第四步:求和

代码如下:

java 复制代码
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++) {
	    //从前向后遍历,遇到负数将其变为正数,同时K--
	    if (nums[i] < 0 && K > 0) {
	    	nums[i] = -nums[i];
	    	K--;
	    }
	}
	// 如果K还大于0,那么反复转变数值最小的元素,将K用完

	if (K % 2 == 1) nums[len - 1] = -nums[len - 1];
	return Arrays.stream(nums).sum();

    }
}

我的思路和上面的差不多,但是写法不一样,我是按照从小到大的排序,每次只改变 n u m s [ 0 nums[0 nums[0,改变完之后,就进行重新排序,进行 k k k次。

代码如下:

java 复制代码
class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        Arrays.sort(nums);
        int result=0;
        int count=k;
        if(nums.length==0) return 0;
        while(k>0){
            nums[0]=-nums[0];
            Arrays.sort(nums);
            k--;
        }
        for(int i=0;i<nums.length;i++){
            result+=nums[i];
        }
        return result;
    }
}

时间复杂度 : O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度 : O ( 1 ) O(1) O(1)



Topic2加油站

题目:

在一条环路上有 n n n 个加油站,其中第 i i i 个加油站有汽油 g a s i gasi gasi 升。你有一辆油箱容量无限的的汽车,从第 i i i个加油站开往第 i + 1 i+1 i+1 个加油站需要消耗汽油 c o s t i costi costi 升。你从其中的一个加油站出发,开始时油箱为空。给定两个整数数组 g a s gas gas 和 c o s t cost cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 − 1 -1 −1 。如果存在解,则保证它是唯一的。

输入: g a s = 1 , 2 , 3 , 4 , 5 , c o s t = 3 , 4 , 5 , 1 , 2 gas = 1,2,3,4,5, cost = 3,4,5,1,2 gas=1,2,3,4,5,cost=3,4,5,1,2
输出: 3 3 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 可为起始索引。

思路:

暴力法:
整体代码如下:

java 复制代码
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int haveOil = 0;
        int index;
        for (int i = 0; i < gas.length; i++) {
            if (gas[i] >= cost[i]) {
                index = i;
                haveOil = 0;
                int count = gas.length;
                while (count > 0) {
                    if (haveOil + gas[index % gas.length] - cost[index % gas.length] < 0)
                        break;
                    haveOil += (gas[index % gas.length] - cost[index % gas.length]);
                    index++;
                    count--;
                }
                if (count == 0)
                    return i;

            }
        }
        return -1;
    }
}

贪心法的步骤:

java 复制代码
class Solution {
    public int canCompleteCircuit(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;
    }
}

时间复杂度 : O ( n ) O(n) O(n)
空间复杂度 : O ( 1 ) O(1) O(1)



Topic3分发糖果

题目:

n n n 个孩子站成一排。给你一个整数数组 r a t i n g s ratings ratings 表示每个孩子的评分。

  • 你需要按照以下要求,给这些孩子分发糖果:
  • 每个孩子至少分配到 1 个糖果。

相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

输入: r a t i n g s = 1 , 0 , 2 ratings = 1,0,2 ratings=1,0,2
输出: 5 5 5
解释: 你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
思路:

这在leetcode上是一道困难的题目,其难点就在于贪心的策略,如果在考虑局部的时候想两边兼顾,就会顾此失彼。

那么本题我采用了两次贪心的策略:

一次是从左到右遍历,只比较右边孩子评分比左边大的情况。

一次是从右到左遍历,只比较左边孩子评分比右边大的情况。

这样从局部最优推出了全局最优,即:相邻的孩子中,评分高的孩子获得更多的糖果

代码实现如下:

java 复制代码
class Solution {
    public int candy(int[] ratings) {
        if (ratings.length == 1)
            return 1;
        int[] candy = new int[ratings.length];
        Arrays.fill(candy, 1);
        // 一次是从左到右遍历,只比较右边孩子评分比左边大的情况
        for (int i = 1; i < ratings.length; i++) {
            if (ratings[i] > ratings[i - 1])
                candy[i] = candy[i - 1] + 1;
        }
        // 一次是从右到左遍历,只比较左边孩子评分比右边大的情况
        for (int i = ratings.length - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1])
                candy[i] = Integer.max(candy[i + 1] + 1, candy[i]);
        }
        int result = 0;
        for (int i = 0; i < ratings.length; i++) {
            result += candy[i];
        }
        return result;

    }
}

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)



相关推荐
Jerry12 小时前
LeetCode 28. 找出字符串中第一个匹配项的下标
算法
Jerry14 小时前
LeetCode 459. 重复的子字符串
算法
闲猫16 小时前
Spring AI 对接Deepseek ChatModel 聊天对话
java·前端·spring
海石16 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石16 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
AI云海17 小时前
利用好手机这个工具-碎片化学习
学习
自信的未来18 小时前
JSON 工具|Web Worker 工程化打包 + 语法自动修复 + 多语言代码生成实战
java·前端·json
Brookty18 小时前
【JavaEE】线程安全(一).4:写块串行保安全、CAS
java·开发语言·java-ee·多线程·线程安全
Jerry18 小时前
LeetCode 151. 反转字符串中的单词
算法