Leecode 101刷题笔记之第二章:和你一起你轻松刷题(Python)

简介: 本文是关于LeetCode 101刷题笔记的第二章,主要介绍了使用Python解决贪心算法题目的方法和实例。

455. Assign Cookies (Easy)

题目描述
有一群孩子和一堆饼干,每个孩子有一个饥饿度,每个饼干都有一个大小。每个孩子只能吃
最多一个饼干,且只有饼干的大小大于孩子的饥饿度时,这个孩子才能吃饱。求解最多有多少孩
子可以吃饱。
输入输出样例
输入两个数组,分别代表孩子的饥饿度和饼干的大小。输出最多有多少孩子可以吃饱的数
量。

Input: [1,2], [1,2,3]
Output: 2

在这个样例中,我们可以给两个孩子喂[1,2]、[1,3]、[2,3] 这三种组合的任意一种。

题解
因为饥饿度最小的孩子最容易吃饱,所以我们先考虑这个孩子。为了尽量使得剩下的饼干可
以满足饥饿度更大的孩子,所以我们应该把大于等于这个孩子饥饿度的、且大小最小的饼干给这
个孩子。满足了这个孩子之后,我们采取同样的策略,考虑剩下孩子里饥饿度最小的孩子,直到
没有满足条件的饼干存在。
简而言之,这里的贪心策略是,给剩余孩子里最小饥饿度的孩子分配最小的能饱腹的饼干。
至于具体实现,因为我们需要获得大小关系,一个便捷的方法就是把孩子和饼干分别排序。
这样我们就可以从饥饿度最小的孩子和大小最小的饼干出发,计算有多少个对子可以满足条件。

具体代码
耗时:40ms 61.49
内存消耗:13.8MB 88.75

def findContentChildren(g, s):
    # 孩子数量/小饼干数量
    # 一个孩子只能吃最多一个饼干
    # 先对孩子和饼干的大小进行排序
    g=sorted(g)
    s=sorted(s)
    child,cookie=0,0
    while child<len(g) and cookie<len(s):
        if g[child]<=s[cookie]:
            child+=1
            cookie+=1
        else:
            cookie+=1
    return child

135. Candy (Hard)

题目描述
一群孩子站成一排,每一个孩子有自己的评分。现在需要给这些孩子发糖果,规则是如果一
个孩子的评分比自己身旁的一个孩子要高,那么这个孩子就必须得到比身旁孩子更多的糖果;所
有孩子至少要有一个糖果。求解最少需要多少个糖果。
输入输出样例
输入是一个数组,表示孩子的评分。输出是最少糖果的数量。

Input: [1,0,2]
Output: 5

在这个样例中,最少的糖果分法是[2,1,2]。
题解
做完了题目455,你会不会认为存在比较关系的贪心策略一定需要排序或是选择?虽然这一
道题也是运用贪心策略,但我们只需要简单的两次遍历即可:把所有孩子的糖果数初始化为1;
先从左往右遍历一遍,如果右边孩子的评分比左边的高,则右边孩子的糖果数更新为左边孩子的
糖果数加1;再从右往左遍历一遍,如果左边孩子的评分比右边的高,且左边孩子当前的糖果数
不大于右边孩子的糖果数,则左边孩子的糖果数更新为右边孩子的糖果数加1。通过这两次遍历,
分配的糖果就可以满足题目要求了。这里的贪心策略即为,在每次遍历中,只考虑并更新相邻一
侧的大小关系。
在样例中,我们初始化糖果分配为[1,1,1],第一次遍历更新后的结果为[1,1,2],第二次遍历
更新后的结果为[2,1,2]。
具体代码
耗时:36ms 77.31%
内存消耗:15.4MB 18.49%

def candy(ratings):
    if len(ratings)<2: 
        return len(ratings)
    # 把所有孩子的糖果数初始化为1
    n=[1]*len(ratings)
    # 如果右边孩子的评分比左边的高,则右边孩子的糖果数更新为左边孩子的糖果数加1
    for i in range(1,len(ratings)):
        if ratings[i]>ratings[i-1]:
            n[i]=n[i-1]+1
    # 再从右往左遍历一遍,如果左边孩子的评分比右边的高,且左边孩子当前的糖果数
    # 不大于右边孩子的糖果数,则左边孩子的糖果数更新为右边孩子的糖果数加1
    for j in range(len(ratings)-1,0,-1):
        if ratings[j]<ratings[j-1]:
            n[j-1]=max(n[j-1],n[j]+1)
    return sum(n)

435. Non-overlapping Intervals (Medium)

题目描述
给定多个区间,计算让这些区间互不重叠所需要移除区间的最少个数。起止相连不算重叠。
输入输出样例
输入是一个数组,数组由多个长度固定为2 的数组组成,表示区间的开始和结尾。输出一个
整数,表示需要移除的区间数量。

Input: [[1,2], [2,4], [1,3]]
Output: 1

在这个样例中,我们可以移除区间[1,3],使得剩余的区间[[1,2], [2,4]] 互不重叠。
题解
在选择要保留区间时,区间的结尾十分重要:选择的区间结尾越小,余留给其它区间的空间
就越大,就越能保留更多的区间。因此,我们采取的贪心策略为,优先保留结尾小且不相交的区
间。具体实现方法为,先把区间按照结尾的大小进行增序排序,每次选择结尾最小且和前一个选
择的区间不重叠的区间。我们这里使用C++ 的Lambda,结合std::sort() 函数进行自定义排
序。在样例中,排序后的数组为[[1,2], [1,3], [2,4]]。按照我们的贪心策略,首先初始化为区间
[1,2];由于[1,3] 与[1,2] 相交,我们跳过该区间;由于[2,4] 与[1,2] 不相交,我们将其保留。因
此最终保留的区间为[[1,2], [2,4]]。
具体代码

"""
执行耗时:248 ms,击败了81.74% 的Python用户
内存消耗:44.6 MB,击败了20.78% 的Python用户
"""
def eraseOverlapIntervals(intervals):
    intervals = sorted(intervals, key=lambda x: x[1])
    # 将排序后的第一个值取出来
    new_erase=intervals[0]
    cout=0
    for i in intervals[1:]:
        if i[0]>=new_erase[1]:
            # 说明i在新区间右边
            new_erase[1]=i[1]
        elif i[1]<=new_erase[0]:
            # 说明i在新区间左边
            new_erase[0]=i[0]
        else:
            # 说明i不属于新区间
            cout+=1
    return cout

"""
执行耗时:228 ms,击败了93.38% 的Python用户
内存消耗:44.6 MB,击败了24.20% 的Python用户
"""
def eraseOverlapIntervals1(intervals):
    if not intervals:
        return 0
    intervals = sorted(intervals, key=lambda x: x[1])
    # 将排序后的第一个值取出来
    new_erase=intervals[0]
    cout=0
    pre=new_erase[1]
    for i in intervals[1:]:
        if i[0]<pre:
            cout+=1
        else:
            pre=i[1]
    return cout

605. Can Place Flowers (Easy)

采取什么样的贪心策略,可以种植最多的花朵呢?
本题涉及到最优种花问题,使用贪心算法,每(0,0,0)可以种植一棵(边界除外)
解题技巧,在边界前和后再填充一个数,这样可以解决所有边界出现的特殊情况(如只有一个值等)

具体代码

"""
执行耗时:40 ms,击败了14.53% 的Python用户
内存消耗:13.5 MB,击败了73.31% 的Python用户
"""
class Solution(object):
    def canPlaceFlowers(self, flowerbed, n):
        """
        :type flowerbed: List[int]
        :type n: int
        :rtype: bool
        """
        flowerbed = [0] + flowerbed + [0]
        for i in range(1, len(flowerbed) - 1):
            if [flowerbed[i - 1], flowerbed[i], flowerbed[i + 1]] == [0, 0, 0]:
                n -= 1
                flowerbed[i] = 1  # 说明此位置已经种花了
        if n > 0:
            return False
        else:
            return True

452. Minimum Number of Arrows to Burst Balloons (Medium)

这道题和题目435 十分类似,但是稍有不同,具体是哪里不同呢?
首先排序,以第一个区间的右边界作为对比,如果下一个区间的左边界比它小,则说明用一支箭可以射中两个区间,否则,两个区间是不重叠的,需要两支箭。
具体代码

"""
执行耗时:212 ms,击败了95.12% 的Python用户
内存消耗:43.2 MB,击败了51.41% 的Python用户
"""
class Solution(object):
    def findMinArrowShots(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        points = sorted(points, key=lambda x: x[1])
        start_area = points[0][1]
        cout = 1
        for i in points[1:]:
            # 如果后区间的第一个值大于等于标准区域的后一个值,说明是不同箭
            # 如果小于则使用一个箭
            if i[0] > start_area:
                cout += 1
                start_area = i[1]
        return cout

763. Partition Labels (Medium)

为了满足你的贪心策略,是否需要一些预处理?

注意:
在处理数组前,统计一遍信息(如频率、个数、第一次出现位置、最后一次出现位置等)可
以使题目难度大幅降低。

解题思路:
对于这道题,一个关键点是利用字典key的唯一性来记载一个字母最大的index是几。
然后,遍历字符串并跟它比较,用max函数来康康是边界大还是当前遍历的这个字母的最大index大,
如果当前遍历字母的最大index大,那就说明超过了边界,那就要更新边界,大概就是这个思路。
结合我说的,康康下面的代码哈。
具体代码

# leetcode submit region begin(Prohibit modification and deletion)
# 解法1
"""
执行耗时:24 ms,击败了66.78% 的Python用户
内存消耗:12.9 MB,击败了81.82% 的Python用户
"""
class Solution(object):
    def partitionLabels(self, s):
        """
        :type s: str
        :rtype: List[int]
        """
        # 使用贪心的思想寻找每个片段可能的最小结束下标
        last = [0] * 26
        for i, ch in enumerate(s):
            last[ord(ch) - ord("a")] = i

        # print(last)
        partition = list()
        start = end = 0
        for i, ch in enumerate(s):
            end = max(end, last[ord(ch) - ord("a")])
            # 由于每个片段访问结束的标志是访问到下标 end,因此对于每个片段,
            # 可以保证当前片段中的每个字母都一定在当前片段中,不可能出现在其他片段,
            # 可以保证同一个字母只会出现在同一个片段。
            if i == end:
                partition.append(end - start + 1)
                start = end + 1

        return partition
# leetcode submit region end(Prohibit modification and deletion)

# 解法2
"""
执行耗时:20 ms,击败了88.11% 的Python用户
内存消耗:12.9 MB,击败了86.01% 的Python用户
"""
class Solution(object):
    def partitionLabels(self, s):
        """
        :type s: str
        :rtype: List[int]
        """
        # 首先找到每个元素出现的最后位置,字典的形式,通过元素找到下标
        last = {c: i for i, c in enumerate(s)}
        # 保存字符串的长度
        partition = list()
        # 设置区间
        start = end = 0
        # 开始遍历元素"ababcbacadefegdehijhklij"
        for i, ch in enumerate(s):
            # 设置尾部
            end = max(end, last[ch])
            # 当区间里所有元素都遍历过
            if i == end:
                partition.append(end - start + 1)
                start = end + 1

        return partition
# leetcode submit region end(Prohibit modification and deletion)

122. Best Time to Buy and Sell Stock II (Medium)

股票交易题型里比较简单的题目,在不限制交易次数的情况下,怎样可以获得最大利润呢?
题目描述:
输入: prices = [7,1,5,3,6,4]
输出: 7 意思是:5-1+6-3=7
解题思路:
题目允许可以多次买卖,因此可以当天买进当天卖出或者是当天卖出当天买进。因此只要后一天的价格比前一天的价格高,就可以进行买卖。即设置一个变量,初始化为0,当后一天的价格比前一天的价格高时,变量累加两天价格的差值。
具体代码

# leetcode submit region begin(Prohibit modification and deletion)
"""
执行耗时:20 ms,击败了74.41% 的Python用户
内存消耗:13.7 MB,击败了89.73% 的Python用户
"""
class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        if not prices:
            return 0
        minprice = 0
        for i in range(1, len(prices)):
            if prices[i] > prices[i - 1]:
                minprice += prices[i] - prices[i - 1]
        return minprice
# leetcode submit region end(Prohibit modification and deletion)

406. Queue Reconstruction by Height (Medium)

温馨提示,这道题可能同时需要排序和插入操作。
题目例子
输入: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]]
题目解释
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。
题目思路

  • 分体题意:身高较高的同学我们先做插入操作,然后身高矮的同学按照k为索引,插入k位置上它前面肯定有k个高个子
  • 排序规则:先按照身高降序排序,然后按照比高个数k升序排序
  • 等排序遍历一遍之后,还有人没有落在最终的位置,我们按照他的k值作为索引插入

具体代码

# leetcode submit region begin(Prohibit modification and deletion)
"""
执行耗时:24 ms,击败了91.45% 的Python用户
内存消耗:13.5 MB,击败了82.66% 的Python用户
"""
class Solution(object):
    def reconstructQueue(self, people):
        """
        :type people: List[List[int]]
        :rtype: List[List[int]]
        """
        # 身高越大在前面,身高相同则人数越少在前面-->第一个元素降序排序,第二个升序排序
        people = sorted(people, key=lambda x: (-x[0], x[1]))
        re = []
        # 根据前面身高大于或等于自己身高的个数进行插入
        for s in people:
            re.insert(s[1], s)
        return re
# leetcode submit region end(Prohibit modification and deletion)

665. Non-decreasing Array (Easy)

需要仔细思考你的贪心策略在各种情况下,是否仍然是最优解。
题目描述
给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。
题目实例
示例 1:
输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

示例 2:
输入: nums = [4,2,1]
输出: false
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

解法思路
遍历一遍,遍历的过程中可能会遇到两种情况需要修改,3 4 2和1 4 2,当遇到3 4 2这种情况时,需要把2改成4;当遇到1 4 2时,需要把4改成1或2。每遇到一次nums[i]>nums[i+1]时,就需要改一次,计数器加1,计数器大于1时则返回false。
具体代码

# leetcode submit region begin(Prohibit modification and deletion)
"""
执行耗时:32 ms,击败了48.24% 的Python用户
内存消耗:13.8 MB,击败了80.59% 的Python用户
"""
class Solution(object):
    def checkPossibility(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        cout = 0
        for i in range(len(nums) - 1):
            if nums[i + 1] < nums[i]:
                # 则为递减
                cout += 1
                if cout > 1:
                    return False
                if i - 1 >= 0:
                    # 针对342  将2改为4
                    if nums[i - 1] > nums[i + 1]:
                        nums[i + 1] = nums[i]
                    else:
                        # 针对142 将4改为1或者2
                        nums[i] = nums[i + 1]
        return True
# leetcode submit region end(Prohibit modification and deletion)
目录
相关文章
|
1月前
|
搜索推荐 Python
Leecode 101刷题笔记之第五章:和你一起你轻松刷题(Python)
这篇文章是关于LeetCode第101章的刷题笔记,涵盖了多种排序算法的Python实现和两个中等难度的编程练习题的解法。
21 3
|
1月前
|
算法 C++ Python
Leecode 101刷题笔记之第四章:和你一起你轻松刷题(Python)
这篇博客是关于LeetCode上使用Python语言解决二分查找问题的刷题笔记,涵盖了从基础到进阶难度的多个题目及其解法。
15 0
|
1月前
|
算法 C++ Python
Leecode 101刷题笔记之第三章:和你一起你轻松刷题(Python)
本文是关于LeetCode算法题的刷题笔记,主要介绍了使用双指针技术解决的一系列算法问题,包括Two Sum II、Merge Sorted Array、Linked List Cycle II等,并提供了详细的题解和Python代码实现。
13 0
|
6天前
|
机器学习/深度学习 人工智能 TensorFlow
人工智能浪潮下的自我修养:从Python编程入门到深度学习实践
【10月更文挑战第39天】本文旨在为初学者提供一条清晰的道路,从Python基础语法的掌握到深度学习领域的探索。我们将通过简明扼要的语言和实际代码示例,引导读者逐步构建起对人工智能技术的理解和应用能力。文章不仅涵盖Python编程的基础,还将深入探讨深度学习的核心概念、工具和实战技巧,帮助读者在AI的浪潮中找到自己的位置。
|
6天前
|
机器学习/深度学习 数据挖掘 Python
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
|
6天前
|
存储 Python
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!
|
8天前
|
设计模式 算法 搜索推荐
Python编程中的设计模式:优雅解决复杂问题的钥匙####
本文将探讨Python编程中几种核心设计模式的应用实例与优势,不涉及具体代码示例,而是聚焦于每种模式背后的设计理念、适用场景及其如何促进代码的可维护性和扩展性。通过理解这些设计模式,开发者可以更加高效地构建软件系统,实现代码复用,提升项目质量。 ####
|
7天前
|
机器学习/深度学习 存储 算法
探索Python编程:从基础到高级应用
【10月更文挑战第38天】本文旨在引导读者从Python的基础知识出发,逐渐深入到高级编程概念。通过简明的语言和实际代码示例,我们将一起探索这门语言的魅力和潜力,理解它如何帮助解决现实问题,并启发我们思考编程在现代社会中的作用和意义。
|
8天前
|
机器学习/深度学习 数据挖掘 开发者
Python编程入门:理解基础语法与编写第一个程序
【10月更文挑战第37天】本文旨在为初学者提供Python编程的初步了解,通过简明的语言和直观的例子,引导读者掌握Python的基础语法,并完成一个简单的程序。我们将从变量、数据类型到控制结构,逐步展开讲解,确保即使是编程新手也能轻松跟上。文章末尾附有完整代码示例,供读者参考和实践。
|
8天前
|
人工智能 数据挖掘 程序员
Python编程入门:从零到英雄
【10月更文挑战第37天】本文将引导你走进Python编程的世界,无论你是初学者还是有一定基础的开发者,都能从中受益。我们将从最基础的语法开始讲解,逐步深入到更复杂的主题,如数据结构、面向对象编程和网络编程等。通过本文的学习,你将能够编写出自己的Python程序,实现各种功能。让我们一起踏上Python编程之旅吧!