当前位置: 首页 > article >正文

LeetCode刷题日记之贪心算法(一)

目录

  • 前言
  • 分发饼干
  • 摆动序列
  • 最大子数组和
  • 总结


前言

作为LeetCode刷题的过程中,贪心算法一直是一个经典的算法思路。在这篇文章中,我将记录自己刷LeetCode贪心算法题的过程,并逐步梳理该算法的核心逻辑,包括如何选择最优解、判断局部最优选择的可行性、如何更新问题状态等。希望通过这一系列的学习记录,能够帮助我进一步掌握贪心算法的精髓,也能给其他正在刷题的小伙伴提供一些参考和思路✍✍✍


分发饼干

LeetCode题目链接

这道题目就是你有一个饼干数组,里面表示你有几块不同大小的饼干,然后呢有一个小孩数组,表示有几个不同胃口大小的小孩,你要分配饼干尽可能让最多的孩子得到满足,然后输出得到满足的孩子数量🤔🤔🤔

请添加图片描述

我们来思路梳理

  • 首先将孩子的胃口值数组 g 和饼干的尺寸数组 s 进行升序排序。从胃口最小的孩子开始,用最小尺寸的饼干去满足。这样可以最大化地利用较小的饼干,避免浪费大饼干🤔🤔🤔(小饼干满足小胃口)
  • 先将孩子的胃口和饼干尺寸排序。从胃口最大的孩子开始,用尽可能大的饼干去满足。这样可以保证较大的饼干不浪费在小胃口的孩子上🤔🤔🤔(大饼干满足大胃口)

这里我们就以第一种策略为例来分析贪心的四个子逻辑

  • 如何判断最优解

    • 最优解的目标是尽可能多地满足孩子的胃口🤔🤔🤔为了达到这个目标,选择一种贪心策略来匹配饼干和孩子的胃口。通过排序和逐一分配饼干,可以确保每次都能做出最优选择——满足当前最小的胃口
  • 判断局部最优选择的可行性

    • 局部最优选择是尽量用最小的饼干去满足胃口最小的孩子🤔🤔🤔这样我们不会浪费大尺寸的饼干,可以为更大胃口的孩子保留大尺寸的饼干
  • 更新问题状态

    • 每当分配一个饼干给孩子时,我们需要移除已分配的饼干,表示该饼干已经用过🤔🤔🤔移除已满足的孩子,表示该孩子已经满足胃口🤔🤔🤔接着处理下一个未满足的孩子和未使用的饼干
  • 重复选择

    • 不断重复上述过程,直到饼干或孩子处理完毕。每次都选择最小的饼干去匹配最小的胃口,保证每次都是局部最优解🤔🤔🤔

贪心算法完整代码如下

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        //将胃口数组和饼干尺寸数组进行升序排序
        Arrays.sort(g);
        Arrays.sort(s);
        //初始化遍历孩子数组和饼干数组的索引
        int childIndex = 0;
        int cookieIndex = 0;

        //重复选择:当还有未处理的孩子和饼干时进行分配
        while(childIndex < g.length && cookieIndex < s.length){
            //判断局部最优选择的可行性
            if(s[cookieIndex] >= g[childIndex])childIndex++;//更新问题状态
            cookieIndex++;//更新问题状态
        }
        return childIndex;//最优解即是被满足孩子的数量
    }
}

摆动序列

LeetCode题目链接

这道题就是要返回一个整数数组nums中最长摆动序列的长度,什么是摆动序列呢?就是像[1,7,4,9,2,5]这种连续数字之间的差在正数与负数之间交替[6,-3,5,-7,3],另外的就是只有一个元素或者是两个不等元素的序列也可以视作摆动序列🤔🤔🤔

请添加图片描述

我们来进行思路梳理

  • 摆动序列从第一个元素开始,在遍历过程中,计算当前元素与前一个元素的差值。如果这个差值与前一个差值的符号相反(正负交替),则这个元素就是摆动序列中的一个关键点,计入摆动序列🤔如果连续的差值符号相同(即一直增加或一直减少),则忽略中间的元素,只保留转折点🤔如果数组中只有一个元素或者两个不相等的元素,也视为摆动序列🤔

我们来梳理贪心算法的四个子逻辑

  • 如何判断最优解
    • 最优解的目标是找到最长的摆动序列,也就是每当差值出现正负交替时,我们就保留当前元素作为摆动序列的一部分。因此,最优解是通过选择能够引发“转折”的元素,构建一个最长的交替变化的子序列🤔🤔🤔
  • 判断局部最优选择的可行性
    • 局部最优选择是:每次遇到符号发生改变时,保留该元素,这意味着当前元素的差值与之前的差值是正负交替的🤔🤔🤔
  • 更新问题状态
    • 每次选择一个摆动的转折点后,更新当前的符号变化状态(上升还是下降),移动到下一个元素,继续判断它是否符合转折点的条件🤔🤔🤔
  • 重复选择
    • 我们从第一个元素开始遍历数组,并每次遇到符号变化时,将其纳入子序列。不断重复这个过程直到遍历完数组🤔🤔🤔

梳理完逻辑已经很清楚了,贪心算法完整代码如下

class Solution {
    public int wiggleMaxLength(int[] nums) {
        //特殊情况
        if(nums.length < 2) return nums.length;

        int count = 1;//摇摆序列计数器
        int prevDiff = 0;//上一个元素的差值初始为0

        for(int i = 1; i < nums.length; i++){//遍历数组找摇摆序列
            int diff = nums[i] - nums[i - 1];//计算当前元素和前一个元素的差值

            //判断局部最优选择的可行性
            if((diff > 0 && prevDiff <= 0) || (diff < 0 && prevDiff >= 0)){
                count++;//摇摆序列长度加一
                prevDiff = diff;//更新差值
            }
        }
        return count;//返回摇摆序列的长度
    }
}

最大子数组和

LeetCode题目链接

这道题和上一道题不同的是,这里是给一个整数数组,要找一个具有最大和的连续子数组(最少包含一个元素)并返回这个最大和🤔🤔🤔

请添加图片描述

我们来思路梳理

  • 在遍历数组时,维护当前的子数组和。如果当前子数组的和为负数,意味着它对后续的元素没有增益效果,因此应舍弃当前子数组,从下一个元素重新开始计算子数组和🤔在遍历过程中,不断更新全局的最大子数组和。如果当前子数组的和大于之前记录的全局最大和,则更新全局最大和🤔

我们来进一步梳理贪心算法的四个子逻辑

  • 如何判断最优解
    • 最优解是找到具有最大和的连续子数组。在遍历过程中,我们需要找到一种贪心策略:选择当前的元素并判断是继续加到当前的子数组中,还是从当前元素重新开始一个新的子数组🤔🤔🤔
  • 判断局部最优选择的可行性
    • 局部最优选择是:当前子数组和如果为负数,就放弃之前的子数组,从当前元素重新开始新的子数组。如果当前子数组和为正,就继续把当前元素加入到子数组中。这样可以确保每一步都选择对未来最有利的子数组🤔🤔🤔
  • 更新问题状态
    • 每次计算当前子数组和时,更新全局最大和 maxSum,并在每一步更新当前子数组和 currSum,判断是否重新开始子数组🤔🤔🤔
  • 重复选择
    • 重复遍历数组中的每个元素,并在每一步更新 currSummaxSum,直到遍历完整个数组🤔🤔🤔

梳理完逻辑我们直接上代码

class Solution {
    public int maxSubArray(int[] nums) {
        int maxSum = nums[0]; //全局最优解初始化
        int currentSum = nums[0]; //当前子数组和

        for(int i = 1; i < nums.length; i++){
            currentSum = Math.max(nums[i], currentSum + nums[i]);//判断局部最优选择的可行性
            maxSum = Math.max(currentSum, maxSum);//更新问题状态
        }
        return maxSum;//返回全局最优解
    }
}

说了一大堆巴拉巴拉,代码就几行🤪🤪🤪


总结

今天的贪心算法第一轮学习的第一天一些逻辑上面的东西可能说的有点多,后续的话将减少不必要的逻辑阐述,我们专注于问题代码的核心逻辑,大家一起加油,奥利给✊✊✊


http://www.kler.cn/news/354263.html

相关文章:

  • Unity3D URP画面品质的上限如何详解
  • 【HarmonyOS NEXT】服务端向终端推送消息——获取Push Token
  • 详细指南:如何使用WildCard升级到ChatGPT 4.0
  • 【React】使用脚手架或Vite包两种方式创建react项目
  • 基于NXP LS1023+FPGA的嵌入式解决方案
  • 计算机视觉算法的演进与应用:从基础理论到前沿技术
  • 服务器和中转机协同工作以提高网络安全
  • 一站式讲解Wireshark网络抓包分析的若干场景、过滤条件及分析方法
  • Vue.js 组件开发全攻略:从基础到高级特性详解
  • 性能测试工具JMeter(二)
  • 《工业领域缺陷检测方案:创新与应用》
  • C/C++ 内存分布与管理:简单易懂的入门指南
  • hive 误删表恢复
  • 前端一键复制解决方案分享
  • Qt中的连接类型
  • 利用PHP爬虫API接口:高效获取数据的艺术
  • ICM20948 DMP代码详解(85)
  • hardhat部署智能合约
  • 面试感受(续)
  • 简单谈谈mysql中的日志 bin log