• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
贞子小白白
博客园    首页    新随笔    联系   管理    订阅  订阅

Leetcode整理一|01贪心算法

 

首先要感谢《谷歌高畅Leetcode刷题笔记》的作者,本系列文章的发表是基于他的C++版本提出的python版本,并且添加一些自己的总结。

贪心算法☺


 

贪心算法的思想就是,每一步最优就可以达到全局最优。

贪心,顾名思义,就是每一步都很贪心,也就是每一步都按照它认为可以达到最优的方法进行。

练习题一 455分配问题

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

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

链接:https://leetcode-cn.com/problems/assign-cookies

输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

 

  这道题目其实并不难想,就是对每个饼干,把它送给能吃下它的胃口最大的人,因为如果胃口小(s[j] >g[i])就有点浪费,所以尽可能要给胃口大的。

  碰到分配问题的时候提前排序是一个比较好的想法,这里g和s的排序方式应该是一样的,都是从小到大;

  对于每块饼干:

  分到了:因为它>=某个g

  没分到:它<小于所有g

  所以代码是这样的:

class Solution(object):
    def findContentChildren(self, g, s):
        """
        :type g: List[int]
        :type s: List[int]
        :rtype: int
        """
        g = sorted(g)
        s = sorted(s)#或者直接s.sort(),这个时候不能重新赋值
        num = 0
        for bingan in s:
            if len(g):
                if bingan < g[0]:
                    continue
                else:
                    num+=1
                    g.pop(0)
        return num

关于sorted和sort函数:https://www.cnblogs.com/hedianzhan/p/9628435.html

这样出来的结果没有一个时间/空间是超过95%以上的。

整理了一下:

函数 sorted() 默认从小到大升序
使用范围 可迭代对象都可以用
使用方法 sorted(itrearble,cmp=None, key=None, reverse=False)#itrearble可以是数组,字符串
特殊使用

sorted({1:'q',3:'c',2:'g'})

#得到[1, 2, 3],是对key排序

sorted({1:'q',3:'c',2:'g'}.values())

#对字典的值:['c', 'g', 'q']

sorted({1:'q',3:'c',2:'g'}.items())

#[(1, 'q'), (2, 'g'), (3, 'c')]

#对键值对组成的元组的列表

key = lambda x:x[1]

#关键词key传入,对元素取值

 因为整理所花费的时间太过可观,接下来尽量只放链接,然后优化思路这样子。作为资源合集

一个大神整理的解法大全,包含很多通过99%的算法:

https://leetcode-cn.com/problems/assign-cookies/solution/dan-shuang-pai-xu-you-xian-dui-lie-9jie-uhwdm/

我写的内存能击败98.7%的:https://leetcode-cn.com/problems/assign-cookies/solution/shuang-zhi-zhen-pythonnei-cun-ji-bai-987-2jk9/


 

练习题二 135Candy问题

这个分发糖果问题,大的孩子一定要得到更多的糖果,每个孩子都至少有一个糖果。

从左往右遍历一次,右边的大于左边的都可以拿到更多糖果;

从右边往左遍历一次,左边大于右边的可以拿到更多糖果。

代码如下:

class Solution(object):
    def candy(self, ratings):
        """
        :type ratings: List[int]
        :rtype: int
        """
        #从左往右发一遍糖果,再从右往左发一遍糖果。
        candy_list = [1]*len(ratings)
        for i in range(1,len(ratings)):
            if ratings[i]>ratings[i-1]:
                candy_list[i]=candy_list[i-1]+1
        for i in range(len(ratings)-1,0,-1):
            if ratings[i-1]>ratings[i]:
                if candy_list[i-1]<=candy_list[i]:
                    candy_list[i-1] = candy_list[i]+1
        return sum(candy_list)

这个算法的时间复杂度超过了94.77%的用户,空间71.07%。

内存有没有可能更优呢?答案给出了常数空间遍历的代码。两个指标都能击败98 95%的用户,真是牛逼!

但是答案的分析就是基于例子的分析的。我们基于之前实现的两次遍历贪心试试:

从左往右的时候,如果右边更高——>+1

        如果右边没有更高—>reset 1——>右边更高——>+1

                       右边没有更高:这个和右边都要加1

class Solution(object):
    def candy(self, ratings):
        """
        :type ratings: List[int]
        :rtype: int
        """
        n = len(ratings)#记录ratings长度
        ret = 1#记录糖果数量
        inc, dec, pre = 1, 0, 1#递增长度,递减长度,当前糖果数量
        for i in range(1, n):
            if ratings[i] >= ratings[i - 1]:
                dec = 0
                pre = (1 if ratings[i] == ratings[i - 1] else pre + 1)
                ret += pre
                inc = pre
            else:
                dec += 1
                if dec == inc:#等长的时候,到顶了,把顶点包进来;
                    dec += 1
                ret += dec
                pre = 1
        
        return ret

练习题三 435 区间问题

移除最少的区间,让剩下的区间不重叠;

就是要让保留下来的区间数量尽可能大;

保留的要尽可能大,那么,对于同一个start,end要尽可能的小

先对数组区间进行排序,如果当前的star一样大:扔掉;如果不一样大,但是新来的尾巴更短:扔掉当前的,换成它;如果不一样大,尾巴也没更短,并且start还没人家的end长:扔掉;剩下就是死里逃生出来的,可以换成它。

class Solution(object):
    def eraseOverlapIntervals(self, intervals):
        """
        :type intervals: List[List[int]]
        :rtype: int
        """
        if not len(intervals):
            return 0
        intervals = sorted(intervals)
        depict_num = 0
        now = intervals[0]
        for i in range(1,len(intervals)):
            if (intervals[i][0] == now[0]):
                depict_num+=1
            elif intervals[i][1] < now[1]:
                # 取尾巴最短的
                now = intervals[i]
                depict_num+=1
            elif intervals[i][0] < now[1]:
                depict_num+=1
            else:
                now = intervals[i]
        return depict_num

课后习题

605.种花问题:第一朵要立刻决定中不中,然后判断i-1,i和i+1是不是都是0.最后一朵要判断i-1;足够贪心,遍历一遍就行。        

class Solution(object):
    def canPlaceFlowers(self, flowerbed, n):
        """
        :type flowerbed: List[int]
        :type n: int
        :rtype: bool
        """
        if n == 0:
            return True
        if len(flowerbed)==1:
            if flowerbed[0]==0 and n<=1:
                return True
            else:
                return False
        nums = 0
        if flowerbed[0]==0 and flowerbed[1]==0:
            flowerbed[0]=1
            nums+=1
        for i in range(1,len(flowerbed)-1):
            if flowerbed[i-1]==0 and flowerbed[i+1]==0 and flowerbed[i]==0:
                flowerbed[i]=1
                nums+=1     
        if flowerbed[len(flowerbed)-1]==0 and flowerbed[len(flowerbed)-2]==0:
            flowerbed[len(flowerbed)-1]=1
            nums+=1
        if nums >= n:
            return True
        else:
            return False

452.引爆气球:看问题一定要分析本质。用最少的箭引爆气球,也就是说是找最小的不重叠区间:

从左往右来说,区间不相交,箭头直接加1;如果相交,移动上一个箭头,移动到相交的这个区间上面来,对于下一个气球,如果最左边在这个区间上,那很省事,否则就重新加一个箭头。

class Solution(object):
    def findMinArrowShots(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        points.sort()#points这里表示成箭头的区间了
        i = 1
        while i < len(points):
            (al, ar), (bl, br) = points[i - 1], points[i]
            if bl <= ar:
                points[i - 1] = bl, min(ar, br)
                points.pop(i)
            else:
                i += 1
        return len(points)

763.划分字母区间:

class Solution(object):
    def partitionLabels(self, S):
        """
        :type S: str
        :rtype: List[int]
        """
        #分析:如果a出现在第一个,那么数组的最后一个a之前的所有字母都必须包含在片段里
        #如果片段里包含其他的字母,其他字母的扩展也同样的
        #直到所有的start到end的都被包含才能进行下一步
        List = []
        while len(S):
            pianduan_dict = S[0]
            S=S[1:]
            number = 1
            while len(pianduan_dict) and len(S):
                if pianduan_dict[0] in S:
                    pianduan_dict+=S[0]
                    S=S[1:]
                    number+=1
                else:
                    pianduan_dict=pianduan_dict[1:]
            List.append(number)
            if len(S)==1:
                List.append(1)
                S = ""
        return List

122.股票交易:处理成当天买入卖出即可

class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        #判断第i-1是不是小于第i个,只有小于的时候,才把i-1屯下来
        #判断第i是不是小于第i+1,如果是,就要卖出
        earn = 0
        for i in range(1,len(prices)):
            if prices[i-1]>prices[i]:
                pass
            elif prices[i-1]<=prices[i]:
                earn = earn+prices[i]-prices[i-1]
        return earn

406.根据身高重建队列:我按官方的思路写了代码。

class Solution(object):
    def reconstructQueue(self, people):
        """
        :type people: List[List[int]]
        :rtype: List[List[int]]
        """
        people.sort(key=lambda x: (x[0], -x[1]))
        people = people[::-1]
        # 得到[[7,0],[7,1],[6,1],[5,0],[5,2],[4,4]]
        # 每次从左往右进行判断即可,因为右边的元素插入左边不会影响左边的数值
        ans = []
        def get_pos(List,person):
            # 得到person应该被放在第几个位置
            height = person[0]
            number = person[1]
            now_number = 0
            for i in range(len(List)):
                if List[i][0]>=height:
                    now_number+=1
                    if now_number==number:
                        return i+1
            return 0
        for i in people:
            if not len(ans):
                ans.append(i)
            else:
                pos = get_pos(ans,i)
                ans.insert(pos,i)
        return ans

看看官方的代码吧:

class Solution(object):
    def reconstructQueue(self, people):
        """
        :type people: List[List[int]]
        :rtype: List[List[int]]
        # 身高从小往大的排列,人数从多往少
        """
        people.sort(key=lambda x: (-x[0], x[1]))
        n = len(people)
        ans = list()
        for person in people:
            ans[person[1]:person[1]] = [person]
        return ans

大概这就是我想退学的原因。ans[person[1]:person[1]]插入元素

其实get_pos得到的就是i[1],因为前面大于等于它的数量给定了呀。修改后的代码为:

class Solution(object):
    def reconstructQueue(self, people):
        """
        :type people: List[List[int]]
        :rtype: List[List[int]]
        """
        people.sort(key=lambda x: (x[0], -x[1]))
        people = people[::-1]
        ans = []
        for i in people:
            if not len(ans):
                ans.append(i)
            else:
                #pos = get_pos(ans,i)
                
                ans.insert(i[1],i)
        return ans

665.非递减数列:暴力破解,每次都扔一个数,直到判断为True;如果找不到,返回False

class Solution(object):
    def checkPossibility(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        def get_flag(List):
            flag = True
            for i in range(1,len(List)):
                if List[i]>=List[i-1]:
                    continue
                else:
                    flag=False
                    return flag
            return flag
        nums2 = nums[:]
        flag = False
        for i in range(len(nums)):
            nums2.pop(i)
            if get_flag(nums2):
                return True
            else:
                nums2 = nums[:]
        return flag

其实这题就是在判断到底是nums[i]出问题了,还是nums[i-1]出问题了。并且用替换继续遍历会比pop删除更好。

class Solution(object):
    def checkPossibility(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        N = len(nums)
        count = 0
        for i in range(1, N):
            if nums[i] < nums[i - 1]:
                count += 1
                if i == 1 or nums[i] >= nums[i - 2]:
                    nums[i - 1] = nums[i]
                else:
                    nums[i] = nums[i - 1]
        return count <= 1

 

posted @ 2021-04-15 01:35  贞子小白白  阅读(141)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3