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

白银挑战-贪心高频问题

1. 区间问题

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

1.1 判断区间是否重叠

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

思路分析

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

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

代码实现

python 复制代码
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
python 复制代码
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 合并区间

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

思路分析

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

区间合并

区间1,区间2 合并

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

代码实现

python 复制代码
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.3 插入区间

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

思路分析

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

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

代码实现

python 复制代码
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
python 复制代码
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

2. 字符串分割

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

思路分析

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

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

具体做法

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

代码实现

python 复制代码
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
python 复制代码
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

3. 加油站问题

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

思路分析

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

优化:

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

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

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

代码实现

python 复制代码
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
        
相关推荐
爱刷碗的苏泓舒15 小时前
PPP-AR 中的参考星选取:数学原理、评价指标与切换处理
算法·gnss·模糊度固定·ppp-ar·星间单差·参考星·卫星端偏差
烬羽18 小时前
递归老写崩?一个"退回"公式,把回溯题变成填空题
javascript·深度学习·算法
闪电悠米18 小时前
力扣hot100-41.缺失的第一个正数-原地哈希详解
数据结构·算法·哈希算法
星空露珠19 小时前
28种颜色对应名称,
开发语言·数据库·算法·游戏·lua
QXWZ_IA21 小时前
桥梁数字孪生怎么落地?
人工智能·科技·算法·智能硬件·政务
Kel21 小时前
输出层与反分词(Output Layer & Detokenization)
人工智能·算法·架构
陕西企来客21 小时前
2026年7月技术好GEO优化方案:算法适配与内容策略
人工智能·算法·机器学习·技术好geo优化
风栖柳白杨21 小时前
【面试】AI算法工程师_空白自测版本
人工智能·算法·面试
闪电悠米1 天前
力扣hot100-73.矩阵置零-标记数组详解
算法·leetcode·矩阵
栋***t1 天前
从“纸质试卷”到“AI智能组卷”,麦塔在线考试系统如何重构出题逻辑?
java·大数据·人工智能·算法·重构