【基础算法】贪心算法基础

news2024/11/27 5:38:00

系列综述:
💞目的:本系列是个人整理为了秋招算法的,整理期间苛求每个知识点,平衡理解简易度与深入程度。
🥰来源:材料主要源于代码随想录进行的,每个算法代码参考leetcode高赞回答和其他平台热门博客,其中也可能含有一些的个人思考。
🤭结语:如果有帮到你的地方,就点个赞关注一下呗,谢谢🎈🎄🌷!!!
🌈数据结构基础知识总结篇


文章目录

    • 一、贪心算法理论基础
      • 定义
    • 二、贪心算法基本题目
      • 摆动序列
      • 53. 最大子数组和
      • 122. 买卖股票的最佳时机 II
      • 55. 跳跃游戏
      • 45. 跳跃游戏 II
      • 1005. K 次取反后最大化的数组和
      • 135. 分发糖果
      • 406. 根据身高重建队列
      • 452. 用最少数量的箭引爆气球
      • 763. 划分字母区间
      • 435. 无重叠区间
      • 56. 合并区间
      • 738. 单调递增的数字
    • 参考博客


😊点此到文末惊喜↩︎


一、贪心算法理论基础

定义

  1. 贪心策略的选择
    • 无后效性:当前做的选择不会影响未来做的选择,则该问题可通过所选择的贪心策略得到最优解
    • 性价比高:贪心算法求得的较优解总能够满足是最优解的一个 1 − 1 e 1 - \frac {1} {e} 1e1(63%) 近似
    • 工程实践:通过对于局部最优举反例的方式,如果不能达到全局最优,则换其他策略
  2. 一般步骤
    • 将问题分解为若干个子问题
    • 找出适合的贪心策略
    • 求解每一个子问题的最优解
    • 将局部最优解堆叠成全局最优解
  3. 贪心框架
    void greedyAlgorithm(container<type> con){
    	// 健壮性检查
    	// 基本初始化
    	// 贪心策略的迭代
    	// 返回结果
    	return ;
    }
    
  4. 贪心策略的选择
    • 图形化:将数据图形化,求特征
    • 路径法:顺序遍历

动态规划对状态空间的遍历构成一张有向无环图,遍历就是该有向无环图的一个拓扑序。有向无环图中的节点对应问题中的「状态」,图中的边则对应状态之间的「转移」,转移的选取就是动态规划中的「决策」。


二、贪心算法基本题目

摆动序列

  1. 376. 摆动序列
    • 贪心策略:将摆动进行图形化,记录峰值个数
    • 本质:以返回值为目标,减少不必要的中间介质。本题不需要进行删除操作处理,只需要统计数组的峰值数量即可
    • 尽量减少工作变量,如本题中,求峰值需要三个工作变量,但是只需要中间的两个差值。
    int wiggleMaxLength(vector<int>& nums) {
        // 健壮性检查
        if (nums.size() <= 1) 
            return nums.size();
        // 初始化
        int curDiff = 0; // 当前一对差值
        int preDiff = 0; // 前一对差值
        int result = 1;  // 记录峰值个数,序列默认序列最右边有一个峰值
        for (int i = 0; i < nums.size() - 1; i++) {
            curDiff = nums[i + 1] - nums[i];
            // 出现峰值
            if ((curDiff > 0 && preDiff <= 0) || (preDiff >= 0 && curDiff < 0)) {
                result++;
                preDiff = curDiff;
            }
        }
        return result;
    }
    

53. 最大子数组和

  1. 53. 最大子数组和
    • 贪心策略:路径法,顺序遍历路径元素,求路径上的最大和,如果出现负数则重开路径
    int wiggleMaxLength(vector<int>& nums) {
        // 健壮性检查
        if (nums.size() <= 1) 
            return nums.size();
        // 初始化
        int curDiff = 0; // 当前一对差值
        int preDiff = 0; // 前一对差值
        int result = 1;  // 记录峰值个数,序列默认序列最右边有一个峰值
        for (int i = 0; i < nums.size() - 1; i++) {
            curDiff = nums[i + 1] - nums[i];
            // 出现峰值
            if ((curDiff > 0 && preDiff <= 0) || (preDiff >= 0 && curDiff < 0)) {
                result++;
                preDiff = curDiff;
            }
        }
        return result;
    }
    

122. 买卖股票的最佳时机 II

  1. 122. 买卖股票的最佳时机 II
    • 图形化:向上的特征即为利润增加
    • 贪心策略
      • 空间开销小:利润>0就买入第二天卖出(贪心法应该单步考虑局部最优解)
      • 时间开销小:只要明天比今天股价高,就买入并持有。(单次买入,只要涨就不卖出)
    int maxProfit(vector<int>& prices) {
        int profit = 0;
        for(int i = 0; i + 1 < prices.size(); ++i) 
            profit += max(prices[i + 1] - prices[i], 0);
        return profit;
    }
    

55. 跳跃游戏

  1. 55. 跳跃游戏
    • 迭代范围也可以进行更改
    • 贪心策略
      • 每次在可选范围内,跳最大的
    bool canJump(vector<int>& nums) {
        int cover = 0;
        if (nums.size() == 1) return true; 
        // 每次更改迭代范围范围  
        for (int i = 0; i <= cover; i++) { 
            cover = max(i + nums[i], cover);
            if (cover >= nums.size() - 1) return true; // 说明可以覆盖到终点了
        }
        return false;
    }
    

45. 跳跃游戏 II

  1. 45. 跳跃游戏 II
    int jump(vector<int>& nums)	{
        int ans = 0;
        int end = 0;
        int maxPos = 0;
        for(int i = 0; i < nums.size() - 1; i++){
            maxPos = max(nums[i] + i, maxPos);
            if(i == end){
                end = maxPos;// 每次从结束位置开始,而不是起跳位置
                ans++;
            }
        }
        return ans;
    }
    
    

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

  1. 1005. K 次取反后最大化的数组和
    • sort的使用:第三个参数为自定义的排序队则,在头文件#include
    • accumulate的使用:第三个参数为累加的初值,在头文件include
    static bool cmp(int a, int b) {
        return abs(a) > abs(b);// 绝对值的从大到小进行排序
    }
    int largestSumAfterKNegations(vector<int>& A, int K) {
    	// 将容器内的元素按照绝对值从大到小进行排序
        sort(A.begin(), A.end(), cmp); 
        // 在K>0的情况下,将负值按照绝对值从大到小依次取反
        for (int i = 0; i < A.size(); i++) { 
            if (A[i] < 0 && K > 0) {
                A[i] *= -1;
                K--;
            }
        }
        // 如果K为奇数,将最小的正数取反
        if (K % 2 == 1) 
        	A[A.size() - 1] *= -1; 
       	// 求和
        return accumulate(A.begin(),A.end(),0);
        // 第三个参数为累加的初值,在头文件include<numeric>
    }
    

135. 分发糖果

  1. 135. 分发糖果
    • 双向遍历进行贪心处理
    int candy(vector<int>& ratings) {
        vector<int> candyVec(ratings.size(), 1);
        // 从前向后
        for (int i = 1; i < ratings.size(); i++) {
            if (ratings[i] > ratings[i - 1]) 
                candyVec[i] = candyVec[i - 1] + 1;
        }
        // 从后向前
        for (int i = ratings.size() - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1] ) {
                candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);
            }
        }
        // 统计结果
        int result = 0;
        for (int i = 0; i < candyVec.size(); i++) result += candyVec[i];
        return result;
    }
    

406. 根据身高重建队列

  1. 406. 根据身高重建队列
    • 两个维度遍历进行贪心处理
    • 常用插入操作使用list进行处理
    • 感觉可以局部最优推出整体最优,而且想不到反例。就可以使用贪心算法。
    // 身高从大到小排(身高相同k小的站前面)
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        if (a[0] == b[0]) // 相等的,数量小的在前
        	return a[1] < b[1];
        return a[0] > b[0];// 其他情况身高高的的在前
    }
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        sort (people.begin(), people.end(), cmp);
        list<vector<int>> que; // list底层是链表实现,插入效率比vector高的多
        
        for (int i = 0; i < people.size(); i++) {// 按序插入
            int position = people[i][1]; // 插入到下标为position的位置
            std::list<vector<int>>::iterator it = que.begin();
            while (position--) { // 寻找在插入位置
                it++;
            }
            que.insert(it, people[i]);
        }
        return vector<vector<int>>(que.begin(), que.end());
    }
    

452. 用最少数量的箭引爆气球

  1. 452. 用最少数量的箭引爆气球
    • 贪心算法通常先进行排序最值处理
    // 基本比较函数的使用:1.const &形参 2. 形参为比较的两个数据元素 3. 返回值为两个形参的比较
    static bool cmp (const vector<int>& a, const vector<int>& b){
        if(a[0] == b[0])
            return a[1] < b[1];
        return a[0] < b[0];
    }
    int findMinArrowShots(vector<vector<int>>& points) {
        if(points.size() == 0)
            return 0;
        int count = 1;// 至少射一支箭
        sort(points.begin(), points.end(), cmp);
        for(int i = 1; i < points.size(); ++i){
            if(points[i-1][1] < points[i][0]){
                count++;             
            }else{// 记录最小下限
                points[i][1] = min(points[i - 1][1], points[i][1]);
            }
        }
        return count;
    }
    

763. 划分字母区间

  1. 763. 划分字母区间
    • 如果只有字母,可以使用数组进行哈希映射。 vector<int> alphabet = {27,0}
    vector<int> partitionLabels(string s) {
        // 统计每个字符的最远坐标
        unordered_map<char, int> umap;
        for(int i = 0; i < s.size(); ++i){
            umap[s[i]] = i;
        }
    	// 初始化
        vector<int> res;
        int left = 0;
        int right = 0;
        // 迭代
        for(int i = 0; i < s.size(); ++i){
            right = max(right, umap[s[i]]); // 找到字符出现的最远边界
            if (i == right) {
                res.push_back(right - left + 1);// 结果记录
                left = i + 1;
            }
        }
        return res;
        
    }
    

435. 无重叠区间

  1. 435. 无重叠区间
    • 能读就不写:求数量的尽量不要改变改变原来数组,减少写操作。
    static bool cmp (const vector<int>& a, const vector<int>& b) {
        return a[1] < b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if (intervals.size() == 0) return 0;
        sort(intervals.begin(), intervals.end(), cmp);
        int count = 1; // 记录非交叉区间的个数
        int end = intervals[0][1]; // 记录区间分割点
        for (int i = 1; i < intervals.size(); i++) {
            if (end <= intervals[i][0]) {// 记录最小的右边界
                end = intervals[i][1];
                count++;
            }
        }
        return intervals.size() - count;
    }
    

56. 合并区间

  1. 56. 合并区间
    • 排序参数lambda表达式的使用
    • 遍历的容器尽量不用动,使用新的结果容器进行处理
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
    	// 健壮性检查
    	if (intervals.size() == 0) 
    		return result; // 区间集合为空直接返回
        vector<vector<int>> result;
        // 排序的参数使用了lambda表达式
        sort(intervals.begin(), intervals.end(), 
        	[](const vector<int>& a, const vector<int>& b){
        		return a[0] < b[0];
        	}
        );
        // 第一个区间就可以放进结果集里,后面如果重叠,在result上直接合并
        result.push_back(intervals[0]); 
    	// 合并区间,只更新右边界就好,
    	// 因为result.back()的左边界一定是最小值,因为我们按照左边界排序的
        for (int i = 1; i < intervals.size(); i++) {
            if (result.back()[1] >= intervals[i][0]) { // 发现重叠区间
                result.back()[1] = max(result.back()[1], intervals[i][1]); 
            } else {
                result.push_back(intervals[i]); // 区间不重叠 
            }
        }
        return result;
    }
    

738. 单调递增的数字

  1. 738. 单调递增的数字
    • 数字转换成字符串处理
    int monotoneIncreasingDigits(int N) {
        string strNum = to_string(N);
        // flag用来标记赋值9从哪里开始
        // 设置为这个默认值,为了防止第二个for循环在flag没有被赋值的情况下执行
        int flag = strNum.size();
        for (int i = strNum.size() - 1; i > 0; i--) {
            if (strNum[i - 1] > strNum[i] ) {
                flag = i;
                strNum[i - 1]--;
            }
        }
        for (int i = flag; i < strNum.size(); i++) {
            strNum[i] = '9';
        }
        return stoi(strNum);
    }
    

少年,我观你骨骼清奇,颖悟绝伦,必成人中龙凤。
不如点赞·收藏·关注一波


🚩点此跳转到首行↩︎

参考博客

  1. 代码随想录
  2. letcode

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/529150.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

对SRC逻辑漏洞挖掘的思考

对SRC逻辑漏洞挖掘的思考 1.限制购买逻辑漏洞一人一单限制差价活动购买限制 2.支付类逻辑漏洞3.接口未授权逻辑漏洞4.越权类逻辑漏洞5.修改返回包进入后台6.任意用户注册7.重置任意用户 1.限制购买逻辑漏洞 一人一单限制 很多厂商都会搞一些活动&#xff0c;在享受优惠的时候…

微服务架构及工作原理!

在移动计算时代&#xff0c;应用程序开发人员应该能够快速部署操作并进行更改&#xff0c;而无需重新部署整个应用程序。结果&#xff0c;它导致了一种称为“微服务”的构建软件的新方法。 微服务是应用程序中独立的小部分&#xff0c;每个部分都完成自己的工作并通过API相互通…

Dubbo学习笔记

目录 简介 Dubbo高可用 集群容错 服务治理 Dubbo线程IO模型 源码层面 Java SPI 的问题 源码解析 简介 Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架&#xff0c;可以和Spring框架无缝集成。 Dubbo提供了…

主流总线通信和系统接口技术

一、关于现场控制总线 现场总线是自动控制领域的计算机局域网&#xff0c;应用在生产现场&#xff0c;在微机测控设备之间实现双向、串行、多节点数字通信&#xff0c;是一种开放式、数字化、多点通信的底层控制网络。 现场总线具有较高的测控能力指数 得益于仪表的微机化&am…

C++实现闭散列/开放定址法

前言 哈希冲突是无法避免的&#xff0c;只能尽可能的减少冲突的可能性&#xff0c;通常我们可以设计适合的哈希函数。但是&#xff0c;哈希冲突还是会发生&#xff0c;那我们如何解决呢&#xff1f; 我们可以使用闭散列/开放定址法的方法&#xff0c;解决哈希冲突 文章目录 前…

世界超高清大会发布重大技术成果:博冠自主创新推动8K摄像机攻关

一、世界超高清大会背景介绍&#xff1a; 近日&#xff0c;由工业和信息化部、国家广播电视总局、中央广播电视总台、广东省人民政府主办的2023世界超高清视频产业发展大会在广州越秀国际会议展览中心盛大召开。自2018年创办以来&#xff0c;大会已成功举办四届&#xff0c;成…

第08讲:搭建 SkyWalking 源码环境,开启征途

搭建 SkyWalking 源码环境 下载 SkyWalking 源码 执行 git clone 命令从 GitHub下载 SkyWalking 源码&#xff0c;如下所示 &#xff1a; git clone gitgithub.com:apache/skywalking.git 切换分支 等待 clone 完成之后&#xff0c;我们通过命令行窗口进入 SkyWalking 源码根…

SSM 三大框架原理、核心技术,运行流程讲解

作者:arrows 来源:https://www.cnblogs.com/arrows/p/10537733.html 一、Spring部分 1、 Spring的运行流程 第一步&#xff1a;加载配置文件ApplicationContext ac new ClassPathXmlApplicationContext(“beans.xml”); &#xff0c;ApplicationContext接口&#xff0c;它由…

存储卡目录变成未知文件?这些技巧能让你恢复数据!

当存储卡的目录变成未知文件时&#xff0c;我们无法直接访问存储卡中的数据。但是&#xff0c;这并不意味着这些数据永远无法恢复。以下是几种可能恢复存储卡数据的方法&#xff1a; 使用数据恢复软件。从互联网上下载并安装专业的数据恢复软件这些软件可以扫描存储卡&#xf…

分布式接口幂等性设计实现

面对分布式架构和微服务复杂的系统架构和网络超时服务器异常等带来的系统稳定性问题&#xff0c;分布式接口的幂等性设计显得尤为重要。本文简要介绍了几种分布式接口幂等性设计实现&#xff0c;包括Token去重机制、乐观锁机制、数据库主键和状态机实现等&#xff0c;以加深理解…

面板安全增强,网站支持反向代理设置,1Panel开源面板v1.2.0发布

2023年5月15日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.2.0版本。 在这一版本中&#xff0c;1Panel着重增强了安全方面的功能&#xff0c;包括安全入口访问、面板SSL设置、网站密码访问等&#xff0c;同时网站新增支持反向代理设置&#xff0c;并带…

JVM学习(三)

1. JAVA 四中引用类型 1.1. 强引用 在 Java 中最常见的就是强引用&#xff0c; 把一个对象赋给一个引用变量&#xff0c;这个引用变量就是一个强引 用。当一个对象被强引用变量引用时 &#xff0c;它处于可达状态&#xff0c;它是不可能被垃圾回收机制回收的&#xff0c;即…

Java阶段二Day21

Java阶段二Day21 文章目录 Java阶段二Day21整合Lombok基础组件1 Lombok简介2 安装和配置 Lombok3 Lombok 注解及其用法3.1 Getter 和 Setter3.2 ToString3.3 AllArgsConstructor 和 NoArgsConstructor3.4 Data 4. 总结5 微博项目优化 Knife4j1 Knife4j的优点2 Knife4j快速上手2…

使用Docker构建的MySQL主从架构:高可用性数据库解决方案

前言 MySQL主从架构&#xff0c;我们已经在vmware虚拟机上实践过了&#xff0c;接下来我们一起探讨在docker中如何使用MySQL主从架构。 &#x1f3e0;个人主页&#xff1a;我是沐风晓月 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是沐风晓月&#xff0c;阿里云社…

《论文阅读》基于提示的知识生成解决对话情感推理难题

《论文阅读》基于提示的知识生成解决对话情感推理难题 前言摘要作者新观点问题定义模型框架Global ModelLocal ModelPrompt Based Knowledge Generation分类器实验结果问题前言 你是否也对于理解论文存在困惑? 你是否也像我之前搜索论文解读,得到只是中文翻译的解读后感到失…

openEuler 成功适配 LeapFive InFive Poros 开发板

近日&#xff0c;openEuler RISC-V 23.03 创新版本在跃昉科技的 Poros 开发板上成功运行。 openEuler 在 Poros 上适配成功&#xff0c;XFCE 桌面启动正常&#xff0c;文件系统、终端模拟器和输入法等相关 GUI 应用也运行流畅&#xff0c;Chromium 浏览器和 LibreOffice 等应用…

【Pm4py第三讲】关于Output

本节用于介绍pm4py中的输出函数&#xff0c;包括日志输出、模型输出、面向对象日志输出等。 1.函数概述 本次主要介绍Pm4py中一些常见的输入函数&#xff0c;总览如下表&#xff1a; 函数名说明write_bpmn()用于写入bpmn模型write_dfg()用于写入dfg模型write_pnml() 用于写入p…

面试之高手回答

1.int与Integer的区别 int与Integer的区别有很多&#xff0c;我简单罗列三个方面 第一个作为成员变量来说Integer的初始值是null&#xff0c;int的初始值是0&#xff1b; 第二个Integer存储在堆内存&#xff0c;int类型是在直接存储在栈空间&#xff1b; 第三个integer是个对象…

项目管理6大避坑技巧

1、拒绝错位战略目标 明确目标方向 做项目&#xff0c;首先需要明确项目目标。项目中有很多目标都很重要&#xff0c;但只有一两个目标是最重要的。在任何时刻&#xff0c;我们主要精力都应该集中在一到两个最重要的目标上。 一般最重要的目标具有以下特点&#xff1a;能够给组…

CSS--空间转换及动画

01-空间转换 空间转换简介 空间&#xff1a;是从坐标轴角度定义的 X 、Y 和 Z 三条坐标轴构成了一个立体空间&#xff0c;Z 轴位置与视线方向相同。空间转换也叫 3D转换属性&#xff1a;transform 平移 transform: translate3d(x, y, z); transform: translateX(); transfor…