【Leetcode】top 100 贪心算法

基础知识补充

贪心算法:在对问题求解时,总是做到局部最优

局部最优并不总能获得整体最优解,但通常能获得近似最优解

题目
121 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

思路:如果day2价格高于day1价格,就假设day1买入day2卖出计算收益,如果day3价格高于day2价格,得假设day1买入day2不处理day3卖出...即需要记录前置的历史最低价,出现更低的价格时更新,而出现更高的价格时计算收益,出现次高价格的收益低于最高收益,不会被记录;

python 复制代码
class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        buy, sell = prices[0], 0
        for val in prices:
            if val > buy: sell = max(val-buy, sell)
            else: buy = val
        return sell
55 跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false

思路:当前坐标元素大于数组长度时,一定能到最后一个下标;否则每一步都跳满,再次判断;若当前坐标元素为0,就选择回退一步,再跳满,否则回退两步再跳满...回退到开头仍无法跳过该处0,则永远不可能到达最后一个下标;

python 复制代码
class Solution(object):
    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        i, n = 0, len(nums)-1
        while i>=0 and i<n:
            if nums[i]: i = i+nums[i]
            else:
                idx = i
                while idx >= 0:
                    if idx+nums[idx]>i: break
                    idx -= 1
                i = idx
        return i>=0

更简洁思路:一次遍历每个元素(除最后),用变量记录当前能到达的最远位置,下标大于最远距离时遍历结束,当遍历结束时判断最远位置是否到达最后一个坐标即可;

python 复制代码
class Solution(object):
    def canJump(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        distance, n = 0, len(nums)-1
        for i, val in enumerate(nums):
            if i > distance: break
            distance = max(distance, i+val)
        
        return distance>=n
45 跳跃游戏II

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

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

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

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

思路:动态规划

dp[i] 表示到达下标 i 的最小跳跃数

转移状态:dp[i] = min(dp[i], dp[k]+1) k+nums[k]>=i

初始状态:dp[0] = 0

python 复制代码
class Solution(object):
    def jump(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        n = len(nums)
        dp = [float('inf')]*n
        dp[0] = 0
        for i in range(n):
            for k in range(i):
                if k+nums[k] >= i:
                    dp[i] = min(dp[i], dp[k]+1)
        return dp[-1]
# 执行用时过长

动态规划的优化

dp[i] 是一个非降序数组,所以在找dp[k]时只要找到第一个符合条件的k即可;

而且对于递增的 i,k也是递增的,不需要回退到0再遍历;

python 复制代码
class Solution(object):
    def jump(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        n = len(nums)
        dp = [float('inf')]*n
        dp[0] = 0
        k = 0
        for i in range(1, n):
            while k+nums[k] < i:
                k+=1
            dp[i] = dp[k]+1    # 由于会进行这个+1操作,要么dp[0]=-1,要么遍历跳过nums[0]
        return dp[-1]

更简洁思路:每次跳跃都要跳到最远

以 [3,1,4,2,8] 为例:3最远可以跳到2,在3-2的范围里搜索下一次能跳跃的最远值;

仍依次遍历数组(除最后),当运动到前一次跳跃的最远值时(边界2),将跳跃次数+1同时更新边界;

python 复制代码
class Solution(object):
    def jump(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        n = len(nums)
        distance, cnt, end = 0, 0, 0
        for i in range(n-1):
            distance = max(distance, i+nums[i])
            if i == end:
                cnt+=1
                end = distance
        return cnt
763 划分字母区间

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。返回一个表示每个字符串片段的长度的列表。

思路片段数尽可能多等于每个片段尽可能短;

对于第一个字符(左界),需要将其最远的同字符(右界)与其共同在一个片段中,然后界内判断还需不需要纳入其他字符的同字符(改变右界)当左右界重合时得到了符合要求的一个最短片段;

因为需要查找同字符的最远位置,事先构造了一个哈希表储存字符和其对应下标;

优化:只保存同字符的最远坐标,可以节省哈希表的空间;

python 复制代码
class Solution(object):
    def partitionLabels(self, s):
        """
        :type s: str
        :rtype: List[int]
        """
        # tmp = []
        tmp = defaultdict(list)

        for i in range(len(s)):    
            # tmp[s[i]] = i
            tmp[s[i]].append(i)

        
        left, right, out = 0,0,[]
        for i in range(len(s)):
            right = max(right, max(tmp[s[i]]))

            if i == right: 
                out.append(right-left+1)
                left, right = i+1, 0
        return out
相关推荐
爱吃生蚝的于勒1 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
ChoSeitaku7 小时前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
DdddJMs__1357 小时前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
Fuxiao___7 小时前
不使用递归的决策树生成算法
算法
我爱工作&工作love我7 小时前
1435:【例题3】曲线 一本通 代替三分
c++·算法
白-胖-子7 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
workflower8 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
好睡凯8 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
Sunyanhui18 小时前
力扣 二叉树的直径-543
算法·leetcode·职场和发展
一个不喜欢and不会代码的码农8 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode