当前位置: 首页 > news >正文

初学python的我开始Leetcode题15-3

提示:100道LeetCode热题15-3主要是动态规划相关,包括四题:最长递增子序列、乘积最大子数组、分割等和子集、最长有效括号。由于初学,所以我的代码部分仅供参考。


前言

这是动态规划的最后几题啦,下一周我们探索多维动态规划!


提示:以下是本篇文章正文内容,下面结果代码仅供参考

题目1:最长递增子序列

1.题目要求:

题目如下:

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

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

示例 3:

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

提示:

  • 1 <= nums.length <= 2500
  • -10^{4} <= nums[i] <= 10^{4}

进阶:

  • 你能将算法的时间复杂度降低到 O(n log(n)) 吗?

代码框架已经提供如下:

class Solution(object):

    def lengthOfLIS(self, nums):

        """

        :type nums: List[int]

        :rtype: int

        """

2.结果代码:

import bisectclass Solution(object):def lengthOfLIS(self, nums):""":type nums: List[int]:rtype: int"""if not nums:return 0tails = []                  # tails[i] 代表长度为 i+1 的 LIS 的最小末尾元素for x in nums:# 在 tails 中找到第一个 >= x 的位置idx = bisect.bisect_left(tails, x)if idx == len(tails):   # x 比 tails 中所有元素都大tails.append(x)else:                   # 否则替换以保持 tails 递增且最小tails[idx] = xreturn len(tails)

说明:

这个算法之所以这样选择,在于它把「求最长递增子序列的长度」转化成了一个看似无关的「维护一个尽可能小的单调数组 tails」。

1.为什么只关心“长度”就可以忘掉所有序列细节

  • 题目只要 长度,不要真正的子序列。

  • 因此,我们不在乎 某个长度对应的具体序列是什么
    只在乎 “长度为 k 的递增子序列能取到的最小末尾值”
    只要这个末尾值越小,后面就有越大机会继续延长。

    把这一观察形式化:
    tails[k] = “所有长度为 k+1 的递增子序列中,最后一个元素的最小值”

2.tails 数组天然单调递增
反证:
假设存在 i < j,但 tails[i] ≥ tails[j]。
那么长度为 j+1 的子序列至少要包含 j+1 个数,
而它的最后一个数 tails[j] 却比 tails[i] 小或相等。
此时把前 j 个数截掉后 i+1 个数仍然递增且最后一个数 ≤ tails[j] ≤ tails[i],
于是我们能用更小的末尾值得到一个长度为 i+1 的子序列,
这与 tails[i] 的“最小”定义矛盾。
所以 tails 必严格单调递增。

有了这个单调性,就可以用 二分查找 在 O(log n) 时间内定位插入位置。

3.算法流程的细节
遍历数组时,对每一个元素 x:

  • 如果 x 比 tails 里所有数都大 → 直接 append,
    相当于“找到一条更长的递增子序列”。

  • 否则,用 x 替换掉 第一个 ≥ x 的位置 上的值。
    这一步不是随意替换,而是 在保证长度不变的前提下把末尾值变小
    从而给后面的数字更多“可接龙”的机会。
    虽然替换后旧序列被“破坏”,但长度信息被完美保留在 tails 的大小里。

由于 tails 始终保持“最小末尾”的性质,
遍历结束时它的长度就是全局最长递增子序列的长度。

一句话总结:
把“最长递增子序列”这一看似需要回溯所有可能组合的问题,压缩成 只维护一个单调数组 tails 的长度,并用二分查找在每一步 O(log n) 时间内完成更新~

题目2:乘积最大子数组

1.题目要求:

题目如下:

给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

测试用例的答案是一个 32-位 整数。

示例 1:

输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。

示例 2:

输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

提示:

  • 1 <= nums.length <= 2 * 10^{4}
  • -10 <= nums[i] <= 10
  • nums 的任何子数组的乘积都 保证 是一个 32-位 整数

代码框架已经提供如下:

class Solution(object):

    def maxProduct(self, nums):

        """

        :type nums: List[int]

        :rtype: int

        """

2.结果代码:

class Solution(object):def maxProduct(self, nums):""":type nums: List[int]:rtype: int"""if not nums:return 0max_prod = nums[0]          # 全局答案imax = imin = nums[0]       # 以当前元素结尾的最大/最小乘积for x in nums[1:]:# 如果 x 为负数,imax 与 imin 会互换if x < 0:imax, imin = imin, imaximax = max(x, imax * x)  # 要么自成一段,要么接在前一段后面imin = min(x, imin * x)  # 同理max_prod = max(max_prod, imax)return max_prod

说明:

乘积类 DP 的典型特征:
负数 × 负数 会变成正数,因此必须同时记住“以 i 结尾的最大乘积”和“最小乘积”。
一次遍历即可,时间 O(n)、空间 O(1)。

题目3:分割等和子集

1.题目要求:

题目如下:

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100

代码框架已经提供如下:

class Solution(object):

    def canPartition(self, nums):

        """

        :type nums: List[int]

        :rtype: bool

        """

2.结果代码:

class Solution(object):def canPartition(self, nums):""":type nums: List[int]:rtype: bool"""total = sum(nums)if total & 1:                 # 总和为奇数,不可能平分return Falsetarget = total // 2n = len(nums)# 一维滚动数组dp = [False] * (target + 1)dp[0] = True                  # 和为 0 总可以for num in nums:# 倒序更新,防止重复选取for s in range(target, num - 1, -1):dp[s] = dp[s] or dp[s - num]return dp[target]

说明:

经典 01 背包 判定:
把问题转化为「能否从数组中选出若干数,使其和为总和的一半」。
若总和为奇数,直接 False;否则做背包即可。

  • 时间:O(n·target)O(200×10^4) 在 Python 中毫无压力。

  • 空间:O(target)

题目4:最长有效括号

1.题目要求:

题目如下:

给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号 子串 的长度。

左右括号匹配,即每个左括号都有对应的右括号将其闭合的字符串是格式正确的,比如 "(()())"

示例 1:

输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"

示例 2:

输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"

示例 3:

输入:s = ""
输出:0

提示:

  • 0 <= s.length <= 3 * 10^{4}
  • s[i] 为 '(' 或 ')'

代码框架已经提供如下:

class Solution(object):

    def longestValidParentheses(self, s):

        """

        :type s: str

        :rtype: int

        """

2.结果代码:

class Solution(object):def longestValidParentheses(self, s):""":type s: str:rtype: int"""stack = [-1]          # 栈底哨兵,方便计算长度max_len = 0for i, ch in enumerate(s):if ch == '(':stack.append(i)else:  # ch == ')'stack.pop()if not stack:           # 弹空说明当前右括号无法匹配stack.append(i)     # 作为新的哨兵else:                   # 匹配成功max_len = max(max_len, i - stack[-1])return max_len

说明:

思路(一次遍历 + 栈):

把「有效括号子串的长度」转化为「两个合法括号之间的索引差」。
维护一个栈,栈底始终放最近一个未匹配左括号的前一个索引,这样每次匹配成功时,栈顶元素就是当前有效子串的起点的前一个位置,长度直接 i - stack[-1] 即可。


总结

针对动态规划的四种题型进行了学习,了解了部分有关动态规划与python的相关知识,大家加油!

http://www.dtcms.com/a/335144.html

相关文章:

  • 从0开始学习Java+AI知识点总结-16.web基础知识
  • [ai-agent]环境简介之沙盒e2b vs daytona
  • 深入解析 @nestjs/typeorm的 forRoot 与 forFeature
  • 新手向:GitCode疑难问题诊疗
  • 搜索算法 (一)- 深度优先和广度优先
  • “openfeign“ 报错Invalid bound statement (not found)
  • windows开机启动软件
  • 低空经济产业链全景解析
  • ISIS区域内、区域间计算
  • 发文暴论!线性注意力is all you need!
  • Windows 操作系统 - Windows 恢复浏览器标题栏颜色
  • VS Code配置MinGW64编译Ipopt库
  • 什么是微前端?
  • 关键点检测(11)-HRNet网络
  • 博士招生 | 香港大学 机器增强认知实验室 招收博士生/实习生/访问学生
  • bilibili视频总结
  • mysql使用group by的时候想显示没有参与聚合的字段怎么办
  • 【开发技巧】VS2022+QT5+OpenCV4.10开发环境搭建QT Creator
  • Geostudio 2018 R2安装后提示:软件不能在虚拟机上运行
  • 关于 Linux 内存管理
  • MySQL 深分页优化与条件分页:把 OFFSET 换成“游标”,再用覆盖索引抄近路
  • WSL 配置文件 wsl.conf 设置
  • IOMMU的2级地址翻译机制及多级(2~5)页表查找
  • 56. 合并区间
  • 计算你的身体质量指数(BMI)
  • SQL183 近三个月未完成试卷数为0的用户完成情况
  • ​江湖四大秘本之一的《英耀篇》​
  • 片料矫平机科普
  • Spring AI架构分析
  • leetcode-139. 单词拆分-C