吃肉的刷题记录2-贪心


贪心

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择策略,以便产生全局最优解的算法思想。这种局部最优的选择通常基于某种特定的度量标准,它试图通过一系列最佳选择来构建最终的解决方案。

贪心算法的特点:

  1. 局部最优选择:在问题求解的每一步中,总是做出在当前看来最好的选择。
  2. 无回溯:所做的选择一旦确定,就不会改变,即算法不会回头去修正之前的选择。
  3. 简单高效:通常比其他算法(如动态规划或分治法)更容易理解和实现。
  4. 不总是得到全局最优解:贪心算法并不总是能得到全局最优解,只有当问题具有贪心选择性质和最优子结构性质时才适用。

贪心算法的应用场景:

  • 活动选择问题:选择尽可能多的不冲突的活动。
  • 霍夫曼编码:用于数据压缩,构建最优前缀码。
  • 最小生成树:如普里姆算法(Prim’s algorithm)和克鲁斯卡尔算法(Kruskal’s algorithm)。
  • Dijkstra算法:寻找图中单源最短路径。
  • 分数背包问题:允许物品分割的问题。

贪心算法的设计步骤:

  1. 证明最优子结构:证明问题可以分解为子问题,并且子问题的解也是最优的。
  2. 定义一个选择性质:定义一种策略,使得每次选择都是局部最优的。
  3. 证明贪心选择:证明局部最优选择能够导致全局最优解。
  4. 构造贪心算法:基于贪心选择性质设计算法。
  5. 证明正确性:通过数学归纳法或其他方式证明算法的正确性。

实现注意事项:

  • 选择标准:需要有一个明确的选择标准来决定哪种选择是最好的。
  • 可行性检查:每一步都要确保所作的选择是可行的,即符合问题的约束条件。
  • 贪心选择的顺序:某些情况下,选择的顺序可能会影响最终的结果。

贪心算法的有效性取决于问题本身的特性。对于某些问题,贪心算法可以快速找到最优解;而对于另一些问题,则可能无法得到最优解。因此,在实际应用中需要对问题进行仔细分析以确定贪心算法是否适用。
当然可以。让我们来看一个简单的贪心算法的例子——硬币找零问题。在这个问题中,我们需要用最少数量的硬币来给出找零。假设我们有无限数量的几种面值的硬币(例如1美分、5美分、10美分和25美分)。我们的目标是最小化找零所需的硬币数量。


例题: leetcode.322.零钱兑换

https://leetcode.cn/problems/coin-change/description/
这道题用贪心只能通过51 / 189
因为贪心只寻找局部最优,
要想全部通过,要使用动态规划

def make_change(coins, amount):
    """
    使用贪心算法计算最少硬币数。
    
    参数:
    coins (list of int): 可供使用的硬币面额列表。
    amount (int): 需要找零的金额。
    
    返回:
    int: 最少需要的硬币数量。
    """
    # 对硬币面额进行降序排序
    coins.sort(reverse=True)
    
    # 初始化结果变量
    coin_count = 0
    
    # 遍历硬币列表
    for coin in coins:
        # 计算当前面额硬币的最大数量
        while amount >= coin:
            amount -= coin
            coin_count += 1
            
    return coin_count

# 定义可用的硬币面额
coins = [25, 10, 5, 1]

# 定义需要找零的金额
amount = 63

# 调用函数并打印结果
print("最少需要的硬币数量:", make_change(coins, amount))

这段代码首先定义了一个 make_change 函数,该函数接收硬币面额列表和需要找零的金额作为输入。它先将硬币面额按降序排列,然后从最大面额开始尝试使用尽可能多的该面额硬币,直到剩余的金额不足以使用更大的面额为止。最后,函数返回所需的最少硬币数量。

注意,这个贪心算法假设硬币的面额是有规律的(例如美国硬币系统),在这种情况下,它总是能给出最少的硬币数量。但对于其他面额的组合,贪心算法可能不会总是给出最优解。例如,如果硬币面额为 [1, 3, 4] 并且需要找零 6,贪心算法会选择两个面额为 3 的硬币,但实际上最优解是两个面额为 3 和一个面额为 1 的硬币。


例题: leetcode 5.最长回文字符串

https://leetcode.cn/problems/longest-palindromic-substring


class Solution:
    def longestPalindrome(self, s: str) -> str:
        # 解题思路1:暴力解法通过70/140,剩余超时,思考超时原因如下:如果s[1:8]不是回文串,就没必要继续看s[1:9]和之后的了,但是暴力没有解决这个问题,还有一些边界也没解决,如果挨个解决太麻烦,所以换个思路。
        # 解题思路2:中心扩展,从左到右遍历s,以当前位置curr为原点,左右扩展,查看最长回文串,分为奇数回文和偶数回文,这样的好处是,如果s[2:3]不是回文串,就不会继续检查[1:4]或者[2:4】之类的。时间复杂度更低。
        len_s = len(s)
        if len_s == 1:
            return s

        max_len = 1
        max_str = s[0]
        for idx, val in enumerate(s):
            # 查找偶数回文串,通过率75/142
            left = idx
            right = idx + 1
            while left >= 0 and right < len_s:
                if s[left] == s[right]:
                    temp_len = right + 1 - left
                    if temp_len > max_len:
                        max_len = temp_len
                        max_str = s[left:right + 1]
                    left -= 1
                    right += 1
                else:
                    break

            # 查找奇数回文串 通过率74/142
            left = idx - 1
            right = idx + 1
            while left >= 0 and right < len_s:
                if s[left] == s[right]:
                    temp_len = right + 1 - left
                    if temp_len > max_len:
                        max_len = temp_len
                        max_str = s[left:right + 1]
                    left -= 1
                    right += 1
                else:
                    break
            # 两种合起来通过

        return max_str


  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值