贪心思想

保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。

1. 分配饼干

每个孩子都有一个满足度 g,每个饼干都有一个大小 s,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。

  1. 给一个孩子的饼干应当尽量小并且又能满足该孩子,这样大饼干才能拿来给满足度比较大的孩子。
  2. 因为满足度最小的孩子最容易得到满足,所以先满足满足度最小的孩子。
class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        if len(g) == 0 or len(s) == 0:
            return 0
        g.sort()
        s.sort()
        gi, si = 0, 0
        while gi < len(g) and si < len(s):
            if g[gi] <= s[si]:
                gi += 1
            si += 1
        return gi

2. 不重叠的区间个数

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2][2,3] 的边界相互“接触”,但没有相互重叠。

输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

移除区间的最小数量 = 区间总个数 - 不重叠区间的个数

求不重叠区间的个数:

  1. 从区间集合 intervals 中选择一个区间 x,这个 x 是在当前所有区间中结束最早的(end 最小)。
  2. 把所有与 x 区间相交的区间从区间集合 intvs 中删除。
  3. 重复步骤 1 和 2,直到 intervals 为空为止。之前选出的那些 x 就是最大不相交子集。
class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        length = len(intervals)
        if length <= 1:
            return 0
        intervals.sort(key=lambda ele:ele[-1])  # 按末位升序排列
        x_end = intervals[0][1]  # 排序后,第一个区间就是 x,取其末位值
        count = 1  # 至少有一个区间不相交
        
        for terval in intervals[1:]:
            if terval[0] >= x_end:
                count += 1  # 找到下一个选择的区间了
                x_end = terval[-1]
        return length - count

3. 投飞镖刺破气球

气球在一个水平数轴上摆放,可以重叠,飞镖垂直投向坐标轴,使得路径上的气球都被刺破。求解最小的投飞镖次数使所有气球都被刺破。

也是计算不重叠的区间个数,不过和上一题的区别在于,[1, 2][2, 3] 在本题中算是重叠区间。

class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        if len(points) == 0:
            return 0
        points.sort(key=lambda ele:ele[-1])  # 按末位升序排列
        count = 1  # 至少有一个区间不相交
        x_end = points[0][1]  # 排序后,第一个区间就是 x,取其末位值

        for point in points:
            start = point[0]
            if start > x_end:
                count += 1  # 找到下一个选择的区间了
                x_end = point[1]
        return count

4. 根据身高和序号重组队列

假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对 (h, k) 表示,其中 h 是这个人的身高, k 是排在这个人前面且身高大于或等于 h 的人数。 编写一个算法来重建这个队列。

输入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

输出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
  • 当升高相等时,应该按 k 值从小到大排列
  • 当升高不等时,应该将当前元素插入到已重组队列中的位置 k 上

为了使插入操作不影响后续的操作,身高较高的学生应该先做插入操作 (升高较矮的学生不会影响升高较高学生的 k 值),否则身高较小的学生原先正确插入的第 k 个位置可能会变成第 k+1 个位置。

身高 h 降序、个数 k 值升序,然后将某个学生插入队列的第 k 个位置中。

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:
            # 将 p 插入到位置 k 处,此时 p 前面大于或等于 p 身高的人数就为 k
            # 而后插入的学生由于身高小于 p,因此不会影响 p 的 k 值
            res.insert(p[1], p)  
        return res

5. 买卖股票最大的收益

一次股票交易包含买入和卖出,只进行一次交易,求最大收益。

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

记录开始到当前位置的最小价格,将这个最小价格作为买入价格,如果当前位置不为最小价格,将当前的价格作为售出价格,查看当前收益是不是最大收益。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n == 0:
            return 0
        min_ = prices[0]
        max_ = 0
        for i in range(1, n):
            if min_ > prices[i]:
                min_ = prices[i]
            else:
                max_ = max(max_, prices[i] - min_)
        return max_

6. 买卖股票最大的收益II

可以进行多次交易,多次交易之间不能交叉进行,可以进行多次交易。

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 

对于 [a, b, c, d],如果有 a <= b <= c <= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i]prices[i] - prices[i - 1] > 0,那么就把 prices[i] - prices[i - 1] 添加到收益中。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        profit = 0
        for i in range(1, len(prices)):
            if prices[i] > prices[i - 1]:
                profit += prices[i] - prices[i - 1]
        return profit

7. 种植花朵

flowerbed 数组中 1 表示已经种下了花朵。花朵之间至少需要一个单位的间隔,求解是否能种下 n 朵花。

输入: flowerbed = [1,0,0,0,1], n = 1
输出: True

记录每个位置的前面后后面的数,如果自身不为 0,且前后均不为 0,则可种下一朵花。

class Solution:
    def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
        len_ = len(flowerbed)
        count = 0
        
        for i in range(len_):
            if count >= n: break
            if flowerbed[i] == 1: continue
                
            pre = 0 if i == 0 else flowerbed[i - 1]
            next_ = 0 if i == len_ - 1 else flowerbed[i + 1]
            if pre == 0 and next_ == 0:
                count += 1
                flowerbed[i] = 1
        return count >= n

8. 判断是否为子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        si, ti = 0, 0
        while si < len(s) and ti < len(t):
            if s[si] == t[ti]:
                si += 1
                ti += 1
            else:
                ti += 1
        return si == len(s)
class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        index_ = -1
        for c in s:
            t = t[index_ + 1:]
            index_ = t.find(c)
            if index_ == -1:
                return False
        return True

9. 修改一个数成为非递减数组

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。

nums[i] < nums[i - 1] 时:

  • 优先考虑令 nums[i - 1] = nums[i],因为如果修改 nums[i] = nums[i - 1] 的话,那么 nums[i] 这个数会变大,就有可能比 nums[i + 1] 大,从而影响了后续操作。
  • 还有一个比较特别的情况就是 nums[i] < nums[i - 2],修改 nums[i - 1] = nums[i] 不能使数组成为非递减数组,只能修改 nums[i] = nums[i - 1]
class Solution:
    def checkPossibility(self, nums: List[int]) -> bool:
        count = 0
        for i in range(1, len(nums)):
            if count >= 2: break
            if nums[i] >= nums[i - 1]: continue
            count += 1
            if i - 2 >= 0 and nums[i - 2] > nums[i]:
                nums[i] = nums[i - 1]
            else:
                nums[i - 1] = nums[i]
        return count <= 1

10. 子数组最大的和

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

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        if len(nums) == 0:
            return 0
        pre_sum = nums[0]
        max_sum = pre_sum
        for i in range(1, len(nums)):
            pre_sum = pre_sum + nums[i] if pre_sum > 0 else nums[i]
            max_sum = max(max_sum, pre_sum)
        return max_sum

11. 分割字符串是同中字符出现在一起

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

输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。

对于遇到的每一个字母,去找这个字母最后一次出现的位置,用来更新当前的最小区间。

  • 定义数组 last[char] 来表示字符 char 最后一次出现的下标。
  • 定义 anchor 和 j 来表示当前区间的首尾。如果遇到的字符最后一次出现的位置下标大于 j, 就让 j = last[c] 来拓展当前的区间。
  • 当遍历到了当前区间的末尾时 (即 i == j ),把当前区间加入答案,同时将 start 设为 i + 1 去找下一个区间。
class Solution:
    def partitionLabels(self, S: str) -> List[int]:
        last = {c: i for i, c in enumerate(S)}
        res = []
        j = anchor = 0
        for i, c in enumerate(S):
            j = max(j, last[c])
            if i == j:
                res.append(i - anchor + 1)
                anchor = i + 1
        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值