《算法通关之路》-chapter14贪心法

《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。

分发饼干

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

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/assign-cookies
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法一:贪心法

'''
方法一:贪心法
时间复杂度:O(glogg+slogs)
空间复杂度:O(1)
'''

class Solution:
    def findContentChildren(self, g: list[int], s: list[int]) -> int:
        g.sort()
        s.sort()
        i, j = len(g) - 1, len(s) - 1
        while i >= 0 and j >= 0:
            if s[j] >= g[i]: # 饼干满足孩子
                j -= 1
                i -= 1
            else:
                i -= 1
        return len(s) - j - 1 # 总饼干数减去剩下的饼干数

g, s = [1,2,3], [1,1]
solu = Solution()
solu.findContentChildren(g, s)

跳跃游戏

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

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

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

方法一:动态规划

'''
方法一:动态规划
时间复杂度:O(n2)
空间复杂度:O(n)
'''

class Solution:
    def canJump(self, nums: list[int]) -> bool:
        dp = [False] * len(nums)
        dp[0] = True # 第一个位置直接到达
        for i in range(len(nums)):
            if dp[i] == False:
                break
            for j in range(i + 1, i + nums[i] + 1):
                if j > len(nums) - 1: # 能到达的长度大于nums长度
                    return True
                dp[j] = True # 能到达的位置设为True
        return dp[-1]

nums = [2,3,1,1,4]
solu = Solution()
solu.canJump(nums)

方法二:动态规划(时间优化)

'''
方法二:动态规划(时间优化)
时间复杂度:O(n)
空间复杂度:O(n)
'''

class Solution:
    def canJump(self, nums: list[int]) -> bool:
        n = len(nums)
        if n == 1: # 长度为1一定可以到达
            return True
        dp = [0] * n
        dp[0] = nums[0] # dp存储当前能到达的最远距离
        for i in range(1, n):
            if dp[i-1] < i: # 中间断开
                return False
            elif dp[i-1] >= n - 1: # 能到达终点
                return True
            else:
                dp[i] = max(dp[i-1], i + nums[i]) # 更新最远能到达的距离
        return True

nums = [2,3,1,1,4]
solu = Solution()
solu.canJump(nums)

方法三:动态规划(空间优化)

'''
方法三:动态规划(空间优化)
时间复杂度:O(n)
空间复杂度:O(1)
'''

class Solution:
    def canJump(self, nums: list[int]) -> bool:
        n = len(nums)
        if n == 1: # 长度为1一定可以到达
            return True
        pre = nums[0] # dp存储当前能到达的最远距离
        for i in range(1, n):
            if pre < i: # 中间断开
                return False
            elif pre >= n - 1: # 能到达终点
                return True
            else:
                pre = max(pre, i + nums[i]) # 更新最远能到达的距离
        return True

nums = [2,3,1,1,4]
solu = Solution()
solu.canJump(nums)

方法四:贪心法:每次在可跳跃范围内选择一个下一次能跳得更远的地方

'''
方法四:贪心法:每次在可跳跃范围内选择一个下一次能跳得更远的地方
时间复杂度:O(n)
空间复杂度:O(1)
'''

class Solution:
    def canJump(self, nums: list[int]) -> bool:
        begin, end = 0, 0
        next_end = end
        while True:
            if end >= len(nums) - 1: # 能到达终点
                return True
            for i in range(begin, end + 1): # 计算最大终点
                next_end = max(next_end, i + nums[i])
            if next_end == end: # 中间断开
                return False
            begin, end = end + 1, next_end # 更新计算区间
        return True

nums = [2,3,1,1,4]
solu = Solution()
solu.canJump(nums)

任务调度器

力扣第621题
给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。

然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。

你需要计算完成所有任务所需要的 最短时间 。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/task-scheduler
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法一:模拟+贪心:优先选择剩余执行次数最多的那个任务

'''
方法一:模拟+贪心:优先选择剩余执行次数最多的那个任务
'''

class Solution:
    def leastInterval(self, tasks: list[str], n: int) -> int:
        n_task, cnt = len(tasks), 0
        task_dict, time_dict = dict(), dict()

        # 初始化
        for i in range(n_task):
            if not task_dict.get(tasks[i]):
                task_dict[tasks[i]] = 0
            task_dict[tasks[i]] += 1
            time_dict[tasks[i]] = 1
        
        # 做任务
        while n_task:

            # 有多种任务不在冷却中,应当选择剩余执行次数最多的那个任务,将每种任务的剩余执行次数尽可能平均
            max_task = 0
            for key in task_dict.keys():
                if time_dict[key] == 1 and task_dict[key] > 0 and task_dict[key] > max_task:
                    max_task = task_dict[key]
                    max_task_id = key
            
            # 执行任务
            if max_task:
                task_dict[max_task_id] -= 1 # 任务数-1
                n_task -= 1 # 总任务数-1
                time_dict[max_task_id] += (n + 1) # 等待时间 + n

            # 更新时间
            for key in time_dict.keys():
                if time_dict[key] > 1:
                    time_dict[key] -= 1
            
            cnt += 1 # 时间计数+1
            
        return cnt

tasks, n = ["A","A","A","B","B","B"], 2
solu = Solution()
solu.leastInterval(tasks, n)

方法二:贪心法

'''
方法二:贪心法
时间复杂度:O(n)
空间复杂度:O(1)
'''

class Solution:
    def leastInterval(self, tasks: list[str], n: int) -> int:
        task_dict = [0] * 26
        for t in tasks:
            task_dict[ord(t) - ord("A")] += 1
        task_dict.sort() # 直接打乱,我们对它们是什么名字不需要了解
        max_num, cnt = task_dict[25], 0
        for i in range(26): # 任务次数最多的任务个数
            if task_dict[i] == max_num:
                cnt += 1
        # 任务次数最多的来搭框架,其余任务来填坑
        return max((max_num - 1) * (n + 1) + cnt, len(tasks))

tasks, n = ["A","A","A","B","B","B"], 2
solu = Solution()
solu.leastInterval(tasks, n)

分发糖果

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/candy
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法一:贪心法

'''
方法一:贪心法
时间复杂度:O(n)
空间复杂度:O(n)
'''
class Solution:
    def candy(self, ratings: list[int]) -> int:
        left_ans, right_ans = [1] * len(ratings), [1] * len(ratings) # 先默认发一个糖果

        # 考虑左相邻孩子条件
        for i in range(1, len(ratings)):
            if ratings[i] > ratings[i-1]:
                left_ans[i] = left_ans[i-1] + 1 # 多发一个

        # 考虑右相邻孩子
        for i in range(len(ratings) - 2, -1, -1):
            if ratings[i] > ratings[i+1]:
                right_ans[i] = right_ans[i+1] + 1 # 多发一个

        # 合并两个结果
        ans = 0
        for i in range(len(ratings)):
            ans += max(left_ans[i], right_ans[i]) # 满足左右

        return ans 

ratings = [1,0,2]
solu = Solution()
solu.candy(ratings)

无重叠区间

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/non-overlapping-intervals
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法一:贪心法:按照区间右端点进行排序

'''
方法一:贪心法:按照区间右端点进行排序
时间复杂度:O(n)
空间复杂度:O(1)
'''

class Solution:
    def eraseOverlapIntervals(self, intervals: list[list[int]]) -> int:
        intervals.sort(key=lambda x:x[1]) # 按照区间右端点进行排序
        cnt = 0
        for i in range(1, len(intervals)):
            # 如果上一个区间的右端点大于这个区间的左端点
            if intervals[i-1][1] > intervals[i][0]:
                intervals[i] = intervals[i-1]
                cnt += 1
        return cnt

intervals = [[1,2],[2,3],[3,4],[1,3]]
solu = Solution()
solu.eraseOverlapIntervals(intervals)

笔记本-Github

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jiawen9

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值