目录

  • 基本思想
  • 简单题目
    • 455 分发饼干
    • 1005 K次取反后最大化的数组和
    • 860 柠檬水找零
  • 序列问题
    • 376 摆动序列
    • 738 单调递增的数字
  • 股票问题
    • 122 买卖股票的最佳时机Ⅱ
  • 两个维度权衡问题
    • 135 分发糖果
    • 406 根据身高重建队列
  • 区间问题
    • 55 跳跃游戏
    • 45 跳跃游戏Ⅱ
    • 452 用最少数量的箭引爆气球
    • 435 无重叠区间
    • 763 划分字母区间
    • 56 合并区间
  • 其他
    • 53 最大子序和
    • 134 加油站
    • 968 监控二叉树

基本思想

选择每一阶段的局部最优,从而达到全局最优

简单题目

455 分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

小饼干喂给胃口小的或者大饼干喂给胃口大的

class Solution:def findContentChildren(self, g: List[int], s: List[int]) -> int:# 大饼干优先满足胃口大的小孩count = 0g = sorted(g)s = sorted(s)indexj = len(s) - 1for i in range(len(g)-1, -1, -1):if indexj >= 0 and s[indexj] >= g[i]:indexj -= 1count += 1return count

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

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

选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。
重复这个过程恰好 k 次。可以多次选择同一个下标 i 。

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

优先把负数转换为正数(优先把那些绝对值最大的负数转化为正数),如果有多余的次数需要对较小的正数进行取反

class Solution:def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:# k优先用于将负数转变为正数# 如果k依旧>0,则转变值最小的元素# 首先按照绝对值从大到小排序nums = sorted(nums, key=abs, reverse=True)ans_min = float('inf')res = 0for i in range(len(nums)):if nums[i] < 0 and k > 0:nums[i] = -nums[i]k -= 1ans_min = min(ans_min, nums[i])res += nums[i]if k % 2 == 0:return reselse:return res - 2*ans_min

860 柠檬水找零

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false。

class Solution:def lemonadeChange(self, bills: List[int]) -> bool:five = 0ten = 0twenty = 0for b in bills:if b == 5:five += 1elif b == 10:ten += 1five -= 1if five < 0:return Falseelif b == 20:# 优先消耗10+5组合,其次考虑3个5组合if ten > 0 and five > 0:ten -= 1five -= 1twenty += 1else:if five >= 3:five -= 3twenty += 1else:return Falsereturn True

序列问题

376 摆动序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。

本质上找出峰值的个数,也可以理解为上升区间和下降区间个数的总和+1

class Solution:def wiggleMaxLength(self, nums: List[int]) -> int:# 其实就是各个峰值的数量if len(nums) <= 1:return len(nums)preDiff = 0curDiff = 0result = 0for i in range(len(nums)-1):curDiff = nums[i+1] - nums[i]if preDiff <= 0 and curDiff > 0 or preDiff >= 0 and curDiff < 0:preDiff =  curDiffresult += 1return result + 1

738 单调递增的数字

当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。

从最低位开始遍历,遇到不满足递增关系的位,将该位后面的全部换为9…,此时不能停止,因为前面仍然可能存在不满足要求的位

class Solution:def monotoneIncreasingDigits(self, n: int) -> int:# 当遇到第i-1位>第i位时,第i-1位--,后面的改成9...# 必须从后向前遍历,找到一个违反递增原则的数字时,不能立刻跳出循环,还需要向前遍历,最终才能保证整体是递增的# 先把整数转换成字符串,最后转成整数str_n = list(str(n))for i in range(len(str_n)-1, 0, -1):if int(str_n[i-1]) > int(str_n[i]):str_n[i-1] = str(int(str_n[i-1])-1)str_n[i:] = '9'*(len(str_n) - i)return int("".join(str_n))

股票问题

122 买卖股票的最佳时机Ⅱ

给定一个数组 prices ,其中 prices[i] 表示股票第 i 天的价格。

在每一天,你可能会决定购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以购买它,然后在 同一天 出售。
返回 你能获得的 最大 利润 。

因为可以在一天之内完成股票的购买和售出,所以可以将股票的最大利润分段化,只有有利可循时才进行购买售出

class Solution:def maxProfit(self, prices: List[int]) -> int:# 贪心,只收集每天的正利润res = 0for i in range(1, len(prices)):a = prices[i] - prices[i-1]if a > 0:res += areturn res

两个维度权衡问题

135 分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

先保证右边孩子比左边孩子获得的糖果更多如果评分更高;再反过来更新一遍

class Solution:def candy(self, ratings: List[int]) -> int:# 先考虑从前往后,右孩子比左孩子大的情况(用左边情况更新右边情况)# 后考虑从后往前,左孩子比右孩子大的情况(用右边情况更新左边情况)record = [1 for i in range(len(ratings))]for i in range(1, len(ratings)):if ratings[i] > ratings[i-1]:record[i] = record[i-1] + 1for i in range(len(ratings)-2, -1, -1):if ratings[i] > ratings[i+1]:record[i] = max(record[i], record[i+1]+1)return sum(record)

406 根据身高重建队列

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

首先按照身高降序排列(为什么不是升序,因为题目中提示ki表示有ki个身高≥hi的人),如果身高相同,按照第二个属性升序排列,最后把第二个属性作为index进行插入

class Solution:def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:# 先对身高高的进行插入,这样可以保证该身高前面没有比他更高的people.sort(key=lambda x:(-x[0], x[1]))res = []for p in people:res.insert(p[1], p)return res

区间问题

55 跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

在每个位置判断一下所能覆盖的最远范围是否包含了最后一个位置

class Solution:def canJump(self, nums: List[int]) -> bool:# 贪心,判断每次跳跃所能到达的覆盖范围是否能到达最末尾res_max = 0if len(nums) == 1:return True# 这里循环的范围是以所能到达的最大范围为准# python中for循环不支持修改其中的变量,用while代替i = 0while i <= res_max:if i + nums[i] > res_max:res_max = i + nums[i]i += 1if res_max >= len(nums) - 1:return Truereturn False

45 跳跃游戏Ⅱ

给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置。

每次跳跃所能达到的最远距离,记录次数

class Solution:def jump(self, nums: List[int]) -> int:# 贪心res = 0cover = 0i = 0if len(nums) == 1:return resprecover = 0while i < len(nums) - 1:if nums[i] + i > cover:cover = nums[i] + i# 每次到达前面一步的最远点,此时必须进行跳跃if i == precover:precover = coverres += 1i += 1return res

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

一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。

对于那些可以用1只弓箭引爆的气球,缩小他们的直径为当前气球和前一个气球的直径末尾的较小值,这样是方便判断那些需要新的弓箭来引爆的气球

class Solution:def findMinArrowShots(self, points: List[List[int]]) -> int:# 首先按照气球起始位置升序points.sort(key = lambda x:x[0])res = 1for i in range(1, len(points)):# 当前区间的起始值大于前面的交集最末尾if points[i][0] > points[i-1][1]:res += 1else:points[i][1] = min(points[i][1], points[i-1][1])return res

435 无重叠区间

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠。

因为对右边界排序了,所以从头开始遍历区间集合,可以贪心选择和前面不交叉的区间;右边界越小越好,只要右边界越小,留给下一个区间的空间就越大,所以从左向右遍历,优先选右边界小的

class Solution:def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:# 首先按照区间结束位置升序排列intervals.sort(key = lambda x:x[1])# 记录不需要移除的最大数量(非交叉区间数量)count = 1end = intervals[0][1]for i in range(1, len(intervals)):if end <= intervals[i][0]:count += 1end = intervals[i][1]return len(intervals) - count

763 划分字母区间

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

当前已遍历的字母中,出现的最远位置

class Solution:def partitionLabels(self, s: str) -> List[int]:# 统计每个字符的最远出现位置a = [-1 for i in range(26)]for i in range(len(s)):a[ord(s[i]) - ord('a')] = i# count = 0res = []left = 0right = 0for i in range(len(s)):idx = ord(s[i]) - ord('a')# 更新最远边界right = max(right, a[idx])# 如果最远边界=当前index,说明这之前都可以划分为一个片段if right == i:res.append(right - left + 1)left = i + 1return res

56 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

class Solution:def merge(self, intervals: List[List[int]]) -> List[List[int]]:res = []intervals.sort(key = lambda x:x[0])end = intervals[0][1]start = intervals[0][0]for i in range(1, len(intervals)):# 把重叠区间融合为一个,所以记录每次区间的末尾if intervals[i][0] <= end:end = max(end, intervals[i][1])else:res.append([start, end])start = intervals[i][0]end = intervals[i][1]res.append([start, end])return res

其他

53 最大子序和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

class Solution:def maxSubArray(self, nums: List[int]) -> int:# 贪心# 只要局部连续子数组和<=0,则重置countres = float('-inf')count = 0for i in range(len(nums)):count += nums[i]if count > res:res = countif count <= 0:count = 0return res

134 加油站

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

可以证明,如果从加油站x可以到达的最后一个加油站是y,那么从x和y之间的任意一个加油站出发都无法到达加油站y的下一个加油站,因此如果找到了无法到达的某个站,则从该站的下一个站继续检查

class Solution:def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:# 当前累加和如果出现了负数,则从j+1开始重新计算resnum = 0totalnum = 0start = 0for i in range(len(gas)):resnum += gas[i] - cost[i]totalnum += gas[i] - cost[i]if resnum < 0:resnum = 0start = i + 1if totalnum < 0:return -1return start

968 监控二叉树

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

自底向上,根据子节点的状态,更新父节点的状态,在状态的转换中更新res数值;局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:def minCameraCover(self, root: TreeNode) -> int:# 0:无覆盖# 1:有摄像头# 2:有覆盖res = 0def tranversal(root):nonlocal resif root == None:return 2left = tranversal(root.left)right = tranversal(root.right)# 左右均有覆盖if left == 2 and right == 2:return 0# 任意一者无覆盖if left == 0 or right == 0:res += 1return 1# 任意一者有摄像头if left == 1 or right == 1:return 2if tranversal(root) == 0:res += 1return res

贪心算法 Greedy相关推荐

  1. 动态规划(Dynamic Programming)与贪心算法(Greedy Algorithm)

    文章目录 动态规划算法(Dynamic Programming) 动态规划问题的属性 应用实例:最长公共子序列问题(Longest Common Subsequence, LCS) 贪心算法(Gree ...

  2. 贪心算法(Greedy Algorithms)

    1.贪心法的设计思想 贪心算法在解决问题的策略上目光短浅, 只根据当前已有的信息就做出选择,而且 一旦做出了选择,不管将来有什么结果,这个选择都不会改变.换言之,贪心法并不是从整体最优考虑,它所做出的 ...

  3. 贪心算法(Greedy Algorithm)最小生成树 克鲁斯卡尔算法(Kruskal#39;s algorithm)

    克鲁斯卡尔算法(Kruskal's algorithm)它既是古典最低的一个简单的了解生成树算法. 这充分反映了这一点贪心算法的精髓.该方法可以通常的图被表示.图选择这里借用Wikipedia在.非常 ...

  4. 贪心算法|Greedy Algorithms(背包问题)

    贪心算法是一种用于优化问题的简单.直观的算法.该算法在寻找整体最优解的过程中,每一步都进行最优选择.贪心算法在一些问题上是非常成功的,例如用于压缩数据的霍夫曼编码,或者用于通过图寻找最短路径的Dijk ...

  5. 贪心算法 (Greedy Algorithm)

    贪心算法 如果要简要得描述这个算法的话就是,首先边的权重排序.(从小到大)循环的判断是否需要选择这里的边.判断的依据则是边的两个顶点是否已经连通,如果连通则继续下一条.不连通就选择使其连通. http ...

  6. leetcode 贪心_贪心算法:给我最好的,现在就要!

    每次做选择的时候都做出当下最好的选择,而不考虑将来的后果.并且期望最终得到的结果是全局最优的. --贪心算法 - Greedy Algorithm 什么时候该使用贪心算法 针对一组数据,定义了限制值. ...

  7. java调度问题的贪心算法_贪心算法——换酒问题

    知识回顾 贪心算法 (greedy algorithm),又称贪婪算法. 是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法. 贪心算法在 有最优子 ...

  8. 贪心算法的最优解条件

    最优子结构条件 证明每次的局部最优解必须在全局最优解序列中,否则不可能到达全局最优 局部最优选择策略的选择很重要 最优化原理,证明该问题具有拟阵结构则贪心算法对该问题的求解是最优解 定理:设 M = ...

  9. 【Java 数据结构 算法】宁可累死自己, 也要卷死别人 18 贪心算法

    [Java 数据结构 & 算法]⚠️宁可累死自己, 也要卷死别人 18⚠️ 贪心算法 概述 贪心算法 电台覆盖问题 代码实现 概述 从今天开始, 小白我将带大家开启 Java 数据结构 &am ...

最新文章

  1. 小程序加载本地图片路径问题
  2. 010_logback中的SocketAppender
  3. Windows 技术篇-利用telnet方法ping端口通不通实例演示,如何测试服务器端口是否启用,windows启用telnet功能
  4. C#---HTML 转文本及HTML内容提取
  5. 数据库设计(概念、步骤)
  6. Kali 2017更新源
  7. ASP.NET进阶(8):HttpModule和HttpApplication
  8. Jquery中如何获取元素的文本,值,属性和内容
  9. redis数据结构对象
  10. 第22篇 js中的this指针的用法
  11. Datepicker-for-Bootstrap 日期选择插件
  12. CSS3最颠覆性的动画效果,基本属性[过渡和2D]
  13. ubuntu 系统 下载GCC FreeType
  14. Python 计算思维训练——公式编程
  15. vm怎么上传镜像文件到服务器,vmware怎么添加iso镜像文件-vmware添加iso镜像文件的方法 - 河东软件园...
  16. 016画笔工具、铅笔工具、颜色替换工具和混合器画笔工具
  17. 目标检测——手把手带你实现SSD(Single Shot MultiBox Detector)训练和检测自己的数据集
  18. SF58-ASEMI快恢复二极管SF58的发展意义
  19. 7段数码管和打印机接口
  20. 【数据结构】两栈共享空间(双端栈)

热门文章

  1. Python nbs***bsp; 乱码
  2. InstructGPT
  3. Director类的使用
  4. 【Node.js】第一章 初识Node.js
  5. 百度地图AR识别SDK免费推出
  6. 计算机素养作文,文明素养的作文优秀范文
  7. ISO/IEC 14443协议浅谈—TYPE A 与 TYPE B 之比较
  8. HAC Ada Compiler(开源Ade编译器)
  9. 黑马程序员——java基础第一课
  10. Linux权限管理(week1_day5)--技术流ken