• 算法通关村第十七关:白银挑战-贪心高频问题


    白银挑战-贪心高频问题

    1. 区间问题

    所有的区间问题,参考下面这张图

    在这里插入图片描述

    1.1 判断区间是否重叠

    LeetCode252
    https://leetcode.cn/problems/meeting-rooms/

    思路分析

    因为一个人在同一时刻只能参加一个会议,因此题目的本质是判断是否存在重叠区间

    1. 将区间按照会议开始时间进行排序
    2. 然后遍历一遍判断后面的会议开始的时候是否前面的还没有结束
    3. 如果出现重叠,返回false

    代码实现

    class Solution:
        def canAttendMeetings(self, intervals: List[List[int]]) -> bool:
            intervals.sort(key=lambda x: x[0])
    
            for i in range(1, len(intervals)):
                if intervals[i][0] < intervals[i - 1][1]:
                    return False
    
            return True
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    class Solution:
        def canAttendMeetings(self, intervals: List[List[int]]) -> bool:
            intervals.sort(key=lambda x: x[0])
            return all(intervals[i][0] >= intervals[i - 1][1] for i in range(1, len(intervals)))
    
    • 1
    • 2
    • 3
    • 4
    1.2 合并区间

    LeetCode 56
    https://leetcode.cn/problems/merge-intervals/

    思路分析

    1. 首先对区间按照起始端点进行升序排序
    2. 然后逐个判断当前区间是否与前一个区间重叠
      如果不重叠,直接加入结果集
      如果重叠,将当前区间与前一个区间进行合并

    区间合并
    区间1,区间2 合并

    [ 区间1起始时间,max(区间1结束时间,区间2结束时间) ]

    代码实现

    class Solution:
        def merge(self, intervals: List[List[int]]) -> List[List[int]]:
            intervals.sort(key=lambda x: x[0])
            merged = []
            for interval in intervals:
                # 合并列表为空
                if not merged:
                    merged.append(interval)
                # 当前区间与上一区间不重叠
                elif interval[0] > merged[-1][1]:
                    merged.append(interval)
                # 当前区间与上一区间重叠,需要合并
                else:
                    # 区间合并操作
                    merged[-1][1] = max(merged[-1][1], interval[1])
    
            return merged
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1.3 插入区间

    LeetCode57
    https://leetcode.cn/problems/insert-interval/

    思路分析

    区间已经按照起始端点升序排序,我们直接遍历区间列表,寻找新区间的插入位置即可

    1. 将新区间左边且相离的区间加入结果集
    2. 接着判断当前区间是否与新区间重叠
      重叠,进行合并,直到遍历到当前区间在新区间右边且相离,加入合并后区间
      不重叠,直接加入新区间
    3. 将新区间右边且相离的区间加入结果集

    代码实现

    class Solution:
        def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
            inserted = []
            index = 0
            n = len(intervals)
    
            # 将新区间左边且相离的区间加入结果集
            while index < n and intervals[index][1] < newInterval[0]:
                inserted.append(intervals[index])
                index += 1
    
            # 接着判断当前区间是否与新区间重叠
            # 重叠,进行合并,直到遍历到当前区间在新区间右边且相离,加入合并后区间
            # 不重叠,直接加入新区间
            while index < n and intervals[index][0] <= newInterval[1]:
                newInterval[0] = min(newInterval[0], intervals[index][0])
                newInterval[1] = max(newInterval[1], intervals[index][1])
                index += 1
            inserted.append(newInterval)
    
            # 将新区间右边且相离的区间加入结果集
            while index < n:
                inserted.append(intervals[index])
                index += 1
    
            return inserted
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    class Solution:
        def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
            inserted = []
            index = 0
            n = len(intervals)
    
            while index < n:
                if intervals[index][1] < newInterval[0]:
                    inserted.append(intervals[index])
                    index += 1
                elif intervals[index][0] <= newInterval[1]:
                    newInterval[0] = min(newInterval[0], intervals[index][0])
                    newInterval[1] = max(newInterval[1], intervals[index][1])
                    index += 1
                else:
                    break
            inserted.append(newInterval)
            inserted.extend(intervals[index:])
            return inserted
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2. 字符串分割

    LeetCode763
    https://leetcode.cn/problems/partition-labels/

    思路分析

    需要把同一个字母圈在同一个区间里

    该遍历过程相当于要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。

    具体做法

    1. 统计每一个字符最后出现的位置
    2. 从头遍历字符,并更新字符最远出现下标,如果找到字符最远出现位置下标和当前下标相等,则找到了分割点

    在这里插入图片描述

    代码实现

    class Solution:
        def partitionLabels(self, s: str) -> List[int]:
            ans = []
    
            # 第一轮遍历,统计每一个字符最后出现的位置
            char_dict = {}
            for i in range(len(s)):
                char_dict[s[i]] = i
    
            # 第二轮遍历
            begin_index = -1
            char_far_index = 0
            for i in range(len(s)):
                char_far_index = max(char_far_index, char_dict[s[i]])
                if char_far_index == i:
                    ans.append(i - begin_index)
                    begin_index = i
    
            return ans
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    class Solution:
        def partitionLabels(self, s: str) -> List[int]:
            last = [0] * 26
            for i, char in enumerate(s):
                last[ord(char) - ord('a')] = i
            
            partition = list()
            start, end = 0, 0
            for i, char in enumerate(s):
                end = max(end, last[ord(char) - ord('a')])
                if i == end:
                    partition.append(end - start + 1)
                    start = end + 1
            
            return partition
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3. 加油站问题

    LeetCode134
    https://leetcode.cn/problems/gas-station/

    思路分析

    很容易想到暴力解法,从第一站开始尝试。缺点就是需要大量的重复计算

    在这里插入图片描述

    优化:
    总油量 - 总消耗 ≥ 0,可以跑完一圈,具体到每一段就是各个加油站的剩油量 rest[i] 相加一定是大于等于0的

    • 每个加油站剩油量 rest[i] = gas[i] - cost[i]
    • i从0开始累加 rest[i] ,得到当前油量 curSum
    • 一旦curSum小于0,说明[0, i]区间都不能作为起始位置,起始位置必须从i+1开始重新算,只有这样才能保证有可能完成

    复杂度降低:从O(n^2)降低到O(n)

    在这里插入图片描述

    代码实现

    
    class Solution:
        def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
            total_sum = 0
            cur_sum = 0
            start = 0
            for i in range(len(gas)):
                cur_sum += gas[i] - cost[i]
                total_sum += gas[i] - cost[i]
                
                # 当前累加rest[i]和 cur_sum小于0
                if cur_sum < 0:
                    # 更新起始位置为 i+1
                    start = i+1
                    # cur_sum从 0 开始
                    cur_sum = 0
                    
            return -1 if total_sum < 0 else start
            
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    kubernetes 调度
    【Java】PAT(Basic Level) 1016 部分A+B
    STM32重要参考资料
    HTTPS(超文本传输安全协议)工作过程
    基于miniprogram-ci的微信小程序的CI以及接入钉钉通知
    基于Hadoop+协同过滤算法+可视化大屏的岗位推荐系统设计与实现(可二开任何招聘系统,校园招聘,公司招聘,计算机岗位招聘)
    U-App移动统计算力升级!支持跨应用、多事件的打包计算
    docker stop了一个docker exec容器,要怎么再启动呢
    【数据结构初阶】常见的排序算法
    使用bert进行文本二分类
  • 原文地址:https://blog.csdn.net/qq_41662142/article/details/132742751