LeetCode笔试刷题一. 贪心算法

一、算法解释

贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的。

二、题型

2.1 分配问题

455. 分发饼干
饥饿度最小的最容易吃饱,将饥饿度从小到大排序,然后饼干也从小到大排序。
456. 分发糖果
糖果问题,一群孩子站成一排,每一个孩子有自己的评分。现在需要给这些孩子发糖果,规则是如果一个孩子的评分比自己身旁的一个孩子要高,那么这个孩子就必须得到比身旁孩子更多的糖果;所有孩子至少要有一个糖果。求解最少需要多少个糖果。
思路:从左到右遍历一遍,再从右到左遍历一遍,每次比较分数和糖果数目(如果分数一样,怎么决定呢~~~~)

2.2 区间问题

435. 无重叠区间问题
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

思路:
我们主要关注区间的结尾,区间的结尾越小,留给其他区间的空间就越大,将区间按结尾排序。每次比较下个区间的开始和前一个区间的结尾即可。

三、练习

605. 种花问题

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。

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

解题思路:

  1. 设一个记录能种的花的数目count。对flowered列表先前后补0,解决边界问题,然后每次判断前一个和后一个是否都为0,是的话就赋值为1,并且count加1。
class Solution:
    def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
        flowerbed = [0] + flowerbed + [0]
        count = 0
        for i in range(1, len(flowerbed) - 1):
            if flowerbed[i] == 0:
                if flowerbed[i - 1] == 0 and flowerbed[i + 1] == 0:
                    flowerbed[i] = 1
                    count += 1
        return count >= n

思路二,设置变量记录0的数目和能种的花的数目。如果有连续的三个0,这说明中间能种一棵花,这时候0的数目从1开始。

class Solution:
    def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
        zero_count, count = 1, 0
        for i in range(len(flowerbed)):
            if flowerbed[i] == 0:
                zero_count += 1
                if zero_count == 3:
                    flowerbed[i-1] = 1
                    zero_count = 1
                    count += 1
            else:
                zero_count = 0
        if zero_count == 2:
            count += 1
        return count >= n

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

思路:这道题有点像无重叠区间。将区间按右端点从低往高排序,每次比较后一个区间的左端点和前一个区间的右端点,如果右端点小于等于左端点,那么可以用同一根箭引爆,这个时候删除列表里的后一个区间,继续比较。

思路二,也可以比较左端点,从低到高排序,这个时候端点的判断要用min函数,不能跟上面一样直接赋值,因为可能出现[[1,10],[3,9],…],这种情况。这个时候重合区间是[3,9]。而如果按右端点排序,那就是[[3,9],[1,10],…],就可以直接删除[1,10],继续后面的比较。

class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        points = sorted(points, key = lambda x: x[0])
        points.sort()
        i, st = 0, 0
        times = len(points) - 1
        while i < times:
            if points[st + 1][0] <= points[st][1]:
                # points[st + 1][1] = points[st][1]
                points[st + 1][1] = min(points[st][1], points[st+1][1])
                del points[st]
            else:
                st += 1
            i += 1
        return len(points)
763 划分字母区间

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
思路:先记录下每个字母出现的最后位置(可以用字典存储)。然后两个指针,一个指向区间开始,一个指向结束。后面遍历字母,对于每个出现的字母,结束指针跑到该字母出现的最后位置,如果遍历到的位置刚好是结束指针的位置,说明前面可以划分为一个区间。

class Solution:
    def partitionLabels(self, s: str) -> List[int]:
        saveDict = {}
        for i, char in enumerate(s):
            saveDict[char] = i
        # print(saveDict)
        st, end = 0, 0
        splitList = []
        for i, char in enumerate(s):
            end = max(end, saveDict[char])
            if i == end:
                splitList.append(end - st + 1)
                st = end + 1
        # print(splitList)
        return splitList

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

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

输入:people = [[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 i k_i ki表示前面升高大于等于 h i h_i hi的人数,身高从高往低排序,后面插入的人不会对前面产生影响。

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值