【算法】【Python3、动态规划、贪心、二分查找】力扣1671. 得到山形数组的最少删除次数

这篇具有很好参考价值的文章主要介绍了【算法】【Python3、动态规划、贪心、二分查找】力扣1671. 得到山形数组的最少删除次数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1671. 得到山形数组的最少删除次数


【算法】【动态规划、贪心、二分查找】力扣1671. 得到山形数组的最少删除次数

问题描述

给定一个整数数组 nums ,我们定义该数组为山形数组当且仅当:

  1. nums 的长度至少为 3。
  2. 存在一个下标 i 满足 0 < i < len(nums) - 1 且:
    • nums[0] < nums[1] < ... < nums[i - 1] < nums[i]
    • nums[i] > nums[i + 1] > ... > nums[len(nums) - 1]

现在,给定整数数组 nums,我们的目标是将其变为山形数组,问最少删除多少个元素。

问题解析

正难则反,我们可以反过来思考原本的nums数组中能组成的最长的山形数组的子序列的长度是多少,最后将数组长度减去这个最长子序列的长度,即为最少删除个数。显然,这是一个最长上升子序列问题。

示例

示例 1:

输入:nums = [1,3,1]
输出:0
解释:数组本身就是山形数组,无需删除元素。

示例 2:

输入:nums = [2,1,1,5,6,2,3,1]
输出:3
解释:一种方法是删除下标为 0、1 和 5 的元素,得到山形数组 [1,5,6,3,1]。

解法一:动态规划

class Solution:
    def minimumMountainRemovals(self, nums: List[int]) -> int:
        n = len(nums)
        # suf[i] 为以i为起点的后缀最长严格下降子序列的长度
        suf = [1] * n
        for i in range(n - 1, -1, -1):
            for j in range(i + 1, n):
                if nums[j] < nums[i]:
                    suf[i] = max(suf[i], suf[j] + 1)
        
        mx = 0
        pre = [1] * n
        for i in range(0, n):
            for j in range(i):
                if nums[j] < nums[i]:
                    pre[i] = max(pre[i], pre[j] + 1)
            if pre[i] >= 2 and suf[i] >= 2:
                mx = max(mx, pre[i] + suf[i] - 1)
        return n - mx

解法分析:

  • 构建两个数组 presuf,分别表示以每个元素为起点的最长上升和下降子序列的长度。
  • 使用两层嵌套循环更新 presuf 数组。
  • 寻找满足条件的元素,计算最大山形数组长度,最终返回删除元素的个数。

复杂度分析:

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( n ) O(n) O(n)

解法二:贪心 + 二分

class Solution:
    def minimumMountainRemovals(self, nums: List[int]) -> int:
        n = len(nums)
        suf = [0] * n
        g = []  # g[i] 代表最长上升子序列长度为 i + 1 的最小元素
        for i in range(n - 1, 0, -1):
            x = nums[i]
            j = bisect_left(g, x)  # 寻找第一个 >= x 的元素的下标
            if j == len(g):  # 代表没有 >= x 的元素
                g.append(x)  # 添加 x 到 g
            else:
                g[j] = x  # 更新最小元素
            suf[i] = j + 1  # j + 1 代表了这个序列的长度。

        mx = 0
        g = []
        pre = 0
        for i in range(0, n, 1):
            x = nums[i]
            j = bisect_left(g, x)
            if j == len(g):
                g.append(x)
            else:
                g[j] = x
            pre = j + 1
            # 题目要求数组长度至少为3,
            # 也就是说,最简单的山形数组的最大值在正中间,左右至少有一个元素
            # 能组成山形数组的子序列也要满足这个要求
            # pre == 1代表左边没有元素,suf[i] == 1代表右边没有元素。
            if pre >= 2 and suf[i] >= 2:
                mx = max(mx, pre + suf[i] - 1)
        return len(nums) - mx

解法分析:

  • 利用贪心思想,维护一个辅助数组 g,其中 g[i] 代表最长上升子序列长度为 i + 1 的最小元素。
  • 使用二分查找更新 g 数组。
  • 遍历数组,计算最大山形数组长度,最终返回删除元素的个数。

复杂度分析:

  • 时间复杂度: O ( n l o g n ) O(n log n) O(nlogn)
  • 空间复杂度: O ( n ) O(n) O(n)

总结

本文介绍了解决力扣1671题的两种方法:动态规划和贪心 + 二分。动态规划方法通过构建两个辅助数组,分别表示以每个元素为起点的最长上升和下降子序列的长度,然后遍历数组寻找满足条件的元素,计算最大山形数组长度。贪心 + 二分方法则利用贪心思想和二分查找,维护一个辅助数组,遍历数组计算最大山形数组长度。两种方法的时间复杂度分别为 O(n^2) 和 O(n log n),空间复杂度均为 O(n)。在实际应用中,可以根据具体情况选择适合的方法。文章来源地址https://www.toymoban.com/news/detail-800287.html

到了这里,关于【算法】【Python3、动态规划、贪心、二分查找】力扣1671. 得到山形数组的最少删除次数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 有效的括号字符串(力扣)动态规划、贪心 JAVA

    给你一个只包含三种字符的字符串,支持的字符类型分别是 ‘(’、‘)’ 和 ‘*’。请你检验这个字符串是否为有效字符串,如果是有效字符串返回true 。 有效字符串符合如下规则: 任何左括号 ‘(’ 必须有相应的右括号 ‘)’。 任何右括号 ‘)’ 必须有相应的左括号 ‘(’

    2024年02月14日
    浏览(40)
  • 【算法】在二维不单调的矩阵上二分查找——力扣1901. 寻找峰值 II

    1901. 寻找峰值 II 给定一个从0开始编号的m x n矩阵 mat ,其中任意两个相邻格子的值都不相同。峰值是指那些严格大于其相邻格子(上、下、左、右)的元素。需要找出任意一个峰值 mat[i][j] 并返回其位置 [i, j] 。 示例 1: 示例 2: 步骤一:列转行 首先,将矩阵的列转换为行,表示为

    2024年02月03日
    浏览(50)
  • Offer必备算法_二分查找_八道力扣OJ题详解(由易到难)

    目录 二分查找算法原理 ①力扣704. 二分查找 解析代码 ②力扣34. 在排序数组中查找元素的第一个和最后一个位置 解析代码 ③力扣69. x 的平方根  解析代码 ④力扣35. 搜索插入位置 解析代码 ⑤力扣852. 山脉数组的峰顶索引 解析代码 ⑥力扣162. 寻找峰值 解析代码 ⑦力扣153. 寻

    2024年02月19日
    浏览(40)
  • LeetCode-1483. 树节点的第 K 个祖先【树 深度优先搜索 广度优先搜索 设计 二分查找 动态规划】

    给你一棵树,树上有 n 个节点,按从 0 到 n-1 编号。树以父节点数组的形式给出,其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。 树节点的第 k 个祖先节点是从该节点到根节点路径上的第 k 个节点。 实现 TreeAncestor 类: TreeAncestor(int n, int[] parent) 对树和父

    2024年04月16日
    浏览(38)
  • 算法 - 动态规划 / 贪心算法

    🥂 🌸 121. 买卖股票的最佳时机 [数组] [股票] (动态规划) 🥂 🌸 122. 买卖股票的最佳时机Ⅱ [数组] [股票] (动态规划) 🥂 🌸 123. 买卖股票的最佳时机Ⅲ [数组] [股票] (动态规划) 🥂 🌸 188. 买卖股票的最佳时机Ⅳ [数组] [股票] (动态规划) 🥂 🌸 309. 买卖股票的最佳时机含冷冻

    2024年01月17日
    浏览(38)
  • 贪心算法和动态规划

      目录 一、简介 二、贪心算法案例:活动选择问题 1.原理介绍 三、动态规划案例:背包问题 1.原理介绍 四、贪心算法与动态规划的区别 五、总结 正则表达式-CSDN博客 深入理解HashMap:Java中的键值对存储利器-CSDN博客   贪心算法和动态规划是两种非常强大的算法设计策略,

    2024年02月05日
    浏览(38)
  • 【力扣·每日一题】2645. 构造有效字符串的最小插入数(动态规划 贪心 滚动数组优化 C++ Go)

    题目链接 给你一个字符串 word ,你可以向其中任何位置插入 “a”、“b” 或 “c” 任意次,返回使 word 有效 需要插入的最少字母数。 如果字符串可以由 “abc” 串联多次得到,则认为该字符串 有效 。 提示: 1 = w o r d . l e n g t h = 50 1 = word.length = 50 1 = w or d . l e n g t h = 50 w

    2024年01月16日
    浏览(50)
  • 四大算法:贪心、分治、回溯、动态规划

    贪心算法(又称贪婪算法),在求解问题时,总是做出在当前看来是最好的选择。也就是说,不从整体最优解进行考虑,而是得到某种意义上的局部最优解。 贪心算法采用自顶向下,以迭代的方法做出贪心选择,每做一次贪心选择,就将所求问题简化为一个规模更小的问题。

    2024年02月15日
    浏览(64)
  • 动态规划与贪心算法的区别

    动态规划和贪心算法都是常见的算法设计技术,它们在很多问题中都有广泛的应用。它们的区别在于: 贪心算法是一种贪心的策略,每一步都采用局部最优的决策,最终得到全局最优解。因此,贪心算法通常解决的是那些具有贪心选择性质的问题,即局部最优解能导致全局最

    2024年02月12日
    浏览(37)
  • 【Python查找算法】二分查找、线性查找、哈希查找

    目录 1 二分查找算法  2 线性查找算法 3 哈希查找算法         二分查找(Binary Search)是一种用于在有序数据集合中查找特定元素的高效算法。它的工作原理基于将数据集合分成两半,然后逐步缩小搜索范围,直到找到目标元素或确定目标元素不存在。 以下是二分查找的

    2024年02月08日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包