力扣hot100题解(python版74-80题)

74、数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 **k** 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示:

  • 1 <= k <= nums.length <= 105
  • -104 <= nums[i] <= 104

思路解答:

方法一:堆排序 构建最小堆 遍历数组每个元素 如果堆大小超过K就弹出堆顶元素 这样堆顶一定为第K大元素

def findKthLargest(nums: list[int], k: int) -> int:
    heap = []
    for num in nums:
        heapq.heappush(heap, num)
        if len(heap) > k:
            heapq.heappop(heap)

    return heap[0]

方法二:排序 切片即可

def findKthLargest(nums: list[int], k: int) -> int:
    return sorted(nums)[len(nums) - k]

75、前K个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

思路解答:

采用哈希表+排序的方式,哈希表key,value统计数字和频率,然后按哈希表的value值排序,最后返回前K个元素

def topKFrequent(nums: list[int], k: int) -> list[int]:
    # 使用 Counter 统计每个元素的频率
    counter = collections.Counter(nums)

    # 对字典按值进行排序,获取前 k 高频率的元素
    sorted_counter = sorted(counter.items(), key=lambda x: x[1], reverse=True)

    # 获取前 k 高频率的元素
    result = [x[0] for x in sorted_counter[:k]]

    return result

76、数据流的中位数

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

  • 例如 arr = [2,3,4] 的中位数是 3
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。
  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。
  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]

解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

提示:

  • -105 <= num <= 105
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 104 次调用 addNumfindMedian

思路解答:

1、addNum 方法首先将元素插入最大堆,然后将最大堆的最大值弹出并插入最小堆。最后,如果最小堆的大小比最大堆大,将最小堆的最小值弹出并插入最大堆,以保持两个堆的平衡。

2、findMedian 方法根据两个堆的大小来返回中位数。如果两个堆的大小相等,取两个堆顶部元素的平均值作为中位数;如果最大堆的大小大于最小堆,直接返回最大堆的顶部元素作为中位数。

注:python中heap模块默认是小根堆

class MedianFinder:

    def __init__(self):
        # 最大堆,存储较小一半的元素
        self.max_heap = []
        # 最小堆,存储较大一半的元素
        self.min_heap = []

    def addNum(self, num: int) -> None:
        # 先将元素插入最大堆
        heapq.heappush(self.max_heap, -num)

        # 将最大堆的最大值弹出并插入最小堆
        heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap))

        # 如果最小堆的大小比最大堆大,将最小堆的最小值弹出并插入最大堆
        if len(self.min_heap) > len(self.max_heap):
            heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap))

    def findMedian(self) -> float:
        if len(self.max_heap) == len(self.min_heap):
            return (self.min_heap[0] - self.max_heap[0]) / 2
        else:
            return -self.max_heap[0]

77、买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

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

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 104

思路解答:

min_price 表示到目前为止的最低价格,max_profit 表示当前的最大利润。在遍历过程中,不断更新这两个变量即可。

def maxProfit(prices: list[int]) -> int:
    min_price = prices[0]
    max_profit = 0

    for price in prices:
        if price < min_price:
            min_price = price
        else:
            max_profit = max(max_profit,price - min_price)

    return max_profit

78、跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

提示:

  • 1 <= nums.length <= 104
  • 0 <= nums[i] <= 105

思路解答:

从后向前遍历数组,不断更新能够到达最后一个位置的最左边的位置。如果最终这个位置是起始位置(第一个下标),则可以到达最后一个下标。

def canJump(nums: list[int]) -> bool:
    last_position = len(nums) - 1

    for i in range(len(nums) - 1, -1, -1):
        #计算当前位置能够跳跃的最远距离
        if i + nums[i] >= last_position:
            last_position = i

    return last_position == 0

79、跳跃游戏II

给定一个长度为 n0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i]
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

示例 2:

输入: nums = [2,3,0,1,4]
输出: 2

提示:

  • 1 <= nums.length <= 104
  • 0 <= nums[i] <= 1000
  • 题目保证可以到达 nums[n-1]

思路解答:

  1. 初始化变量 curr_end 表示当前能够到达的最远位置,curr_farthest 表示在当前能到达的范围内,能够到达的最远位置,jumps 表示跳跃次数,初始值为 0。
  2. 遍历数组,对于当前位置 i,更新 curr_farthestmax(curr_farthest, i + nums[i])
  3. 如果当前位置 i 等于 curr_end,表示已经到达当前能够到达的最远位置,需要进行一次跳跃,更新 curr_endcurr_farthest,同时增加跳跃次数 jumps
  4. curr_end 大于等于 n - 1,即已经能够到达终点,返回跳跃次数 jumps
def jump(nums: list[int]) -> int:
    n = len(nums)
    jumps = 0
    curr_end = 0
    curr_farthest = 0

    for i in range(n - 1):
        curr_farthest = max(curr_farthest, i + nums[i])
        if i == curr_end:
            jumps += 1
            curr_end = curr_farthest
            if curr_end >= n - 1:
                return jumps

    return jumps

80、划分字母区间

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s

返回一个表示每个字符串片段的长度的列表。

示例 1:

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

示例 2:

输入:s = "eccbbbbdec"
输出:[10]

提示:

  • 1 <= s.length <= 500
  • s 仅由小写英文字母组成

思路解答:

  1. 遍历字符串,记录每个字符最后出现的位置。
  2. 使用两个指针 startend 来表示当前片段的起始位置和结束位置。
  3. 遍历字符串,更新当前片段的结束位置 end 为当前字符的最后出现位置的最大值。
  4. 如果当前位置等于 end,表示当前片段已经结束,记录当前片段的长度,并更新 startend + 1
  5. 重复步骤 3 和 4,直到遍历完整个字符串。
def partitionLabels(s: str) -> list[int]:
    
    last_occurrence = {char: i for i, char in enumerate(s)}

    result = []
    start = 0
    end = 0

    for i, char in enumerate(s):
        end = max(end, last_occurrence[char])
        if i == end:
            result.append(end - start + 1)
            start = end + 1

    return result

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目描述 柠檬水找零:在柠檬水摊上,每一杯柠檬水的售价为 5 美。 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一个。 每位顾客只买一杯柠檬水,然后向你支付 5 美、10 美或 20 美。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美。 注意,一开始你手头没有任何零钱。 如果你能给每位顾客正确找零,返回 true ,否则返回 false 示例 1: 输入:[5,5,5,10,20] 输出:true 解释: 前 3 位顾客那里分别支付了 5 美。 第 4 位顾客那里支付了 10 美,接下来是两个 5 美。 第 5 位顾客那里支付了 20 美,接下来是 15 美, 无法提供且返回false。 示例 2: 输入:[5,5,10] 输出:true 示例 3: 输入:[10,10] 输出:false 示例 4: 输入:[5,5,10,10,20] 输出:false 解思路 使用两个变量 five 和 ten 分别表示手头上的 5 美钞票和 10 美钞票的数量。从前往后遍历数组,根据顾客支付的钞票进行分类讨论: 如果顾客支付 5 美,收入 5 美钞票一个。 如果顾客支付 10 美,需要找回一张 5 美钞票,收入 10 美钞票一张和减去一张 5 美钞票。 如果顾客支付 20 美,优先找回一张 10 美和一张 5 美,如果没有再找回三张 5 美,否则收入不够减,返回 false。 算法流程 遍历 bills,记当前手上拥有的 5 美张数 five 和 10 美张数 ten 的数量,初始值为 0。 判断 bills[i] 的大小+0、+5 还是+15,并更新 five 和 ten 的数量。 代码实现 class Solution(object): def lemonadeChange(self, bills): """ :type bills: List[int] :rtype: bool """ five, ten = 0, 0 for bill in bills: if bill == 5: five += 1 elif bill == 10: if not five: return False five -= 1 ten += 1 else: if ten and five: ten -= 1 five -= 1 elif five >= 3: five -= 3 else: return False return True

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值