python算法和数据结构刷题[5]:动态规划

news2025/2/3 9:14:07

动态规划(Dynamic Programming, DP)是一种算法思想,用于解决具有最优子结构的问题。它通过将大问题分解为小问题,并找到这些小问题的最优解,从而得到整个问题的最优解。动态规划与分治法相似,但区别在于动态规划的子问题通常不是相互独立的。

动态规划的核心是解决重复子问题。例如,斐波那

契数列问题,可以通过递归实现,但效率低下,因为会有重复计算。动态规划通过存储已解决的子问题的答案,避免重复计算,从而提高效率。这种方法需要额外的存储空间,是一种空间换时间的策略。

如果一个问题,可以把所有可能的答案穷举出来,并且穷举出来后,发现存在重叠子问题,就可以考虑使用动态规划。

单维动态规划

斐波那契数列

def fibonacci(n):
    # 如果n小于等于1,直接返回n
    if n <= 1:
        return n
    
    # 初始化dp数组,用于存储从0到n的斐波那契数
    dp = [0] * (n + 1)
    dp[1] = 1  # 斐波那契数列的第二个数是1

    # 使用动态规划填充dp数组
    for i in range(2, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]

    # 返回第n个斐波那契数
    return dp[n]

# 测试代码
print(fibonacci(0))  # 输出 0
print(fibonacci(1))  # 输出 1
print(fibonacci(2))  # 输出 1
print(fibonacci(3))  # 输出 2
print(fibonacci(4))  # 输出 3
print(fibonacci(5))  # 输出 5
print(fibonacci(6))  # 输出 8
print(fibonacci(7))  # 输出 13

70. 爬楼梯 - 力扣(LeetCode)

到达第 n 个台阶的方法数等于到达第 n-1 个台阶和第 n-2 个台阶的方法数之和

class Solution:
    def climbStairs(self, n: int) -> int:
        f = [0] * (n + 1)
        f[0] = f[1] = 1
        for i in range(2, n + 1):
            f[i] = f[i - 1] + f[i - 2]
        return f[n]

118. 杨辉三角 - 力扣(LeetCode)

给出一个整数输出杨辉三角 

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        dp = [[0] * i for i in range(1, numRows + 1)]
        
        for i in range(numRows):
            dp[i][0] = 1
            dp[i][i] = 1

        res = []
        for i in range(numRows):
            for j in range(i):
                if i != 0 and j != 0:
                    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]
            res.append(dp[i])

        return res

 322. 零钱兑换 - 力扣(LeetCode)

背包问题:背包问题通常描述为:给定一组物品,每个物品有一定的价值(value)和重量(weight),需要从中选择一些物品放入一个给定容量的背包中,使得放入背包的物品的总价值最大,同时不超过背包的容量。

完全背包问题 

返回构成该金额所需的最少硬币数量。如果该金额无法通过硬币的任何组合来弥补,则返回 。-1

def coinChange(coins, amount):
    # 创建一个数组,长度为 amount + 1,初始值为 amount + 1
    dp = [amount + 1] * (amount + 1)
    # 金额为0时,需要0个硬币
    dp[0] = 0
    
    # 遍历每个金额
    for a in range(1, amount + 1):
        # 遍历每种硬币
        for coin in coins:
            # 如果当前硬币面额小于等于当前金额
            if coin <= a:
                # 更新 dp[a] 为使用当前硬币后的最少硬币数量
                dp[a] = min(dp[a], dp[a - coin] + 1)
    
    # 如果 dp[amount] 还是初始值,则返回 -1,否则返回 dp[amount]
    return dp[amount] if dp[amount] != amount + 1 else -1

# 示例
coins = [1, 2, 5]
amount = 11
print(coinChange(coins, amount))  # 输出应该是 3

279. 完全平方数 - 力扣(LeetCode)

完全背包问题

def numSquares(n):
    # 创建一个数组来存储每个数字的最小完全平方数个数,初始值为0
    dp = [0] * (n + 1)#,用于存储每个数字 i 可以表示为完全平方数之和的最小数量。    
    # 初始化dp数组,每个位置初始为最大值
    for i in range(1, n + 1):
        dp[i] = i  # 最坏的情况是 i 个 1 的平方
    
    # 遍历每个数字,更新其最小完全平方数个数
    for i in range(1, n + 1):
        j = 1
        while j * j <= i:
            dp[i] = min(dp[i], dp[i - j * j] + 1)
            j += 1
    
    # 返回n的最小完全平方数个数
    return dp[n]

# 示例
print(numSquares(12))  # 输出应为 3
print(numSquares(13))  # 输出应为 2,因为 13 = 4 + 9

198. 打家劫舍 - 力扣(LeetCode)

def rob(nums):
    if not nums:
        return 0
    if len(nums) == 1:
        return nums[0]
    dp = [0] * len(nums)
    dp[0] = nums[0]
    dp[1] = max(nums[0], nums[1])
    for i in range(2, len(nums)):
        dp[i] = max(dp[i-1], dp[i-2] + nums[i])
    return dp[-1]

# 示例
nums = [2, 7, 9, 3, 1]
print(rob(nums))  # 输出应该是 12,因为抢第 0、2、4 间房子(2+9+1)

 139. 单词拆分 - 力扣(LeetCode)

def wordBreak(s, wordDict):
    # 首先创建一个布尔数组 dp,长度为字符串长度加一,初始化所有值为 False
    dp = [False] * (len(s) + 1)
    # dp[i] 将表示 s 的前 i 个字符是否可以被成功分割
    dp[0] = True  # 空字符串可以被成功分割

    # 遍历字符串的每一个字符
    for i in range(1, len(s) + 1):
        # 对于每一个可能的结束位置 i,检查所有可能的开始位置 j
        for j in range(i):
            # 如果 dp[j] 为 True,且 s[j:i] 在字典中,那么 dp[i] 也为 True
            if dp[j] and s[j:i] in wordDict:
                dp[i] = True
                break  # 找到一个有效的分割就跳出内层循环

    # 返回 dp[len(s)],它表示整个字符串是否可以被成功分割
    return dp[len(s)]

# 示例用法
swordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
s = "pineapplepenapple"
print(wordBreak(s, swordDict))  # 应该返回 True

 300. 最长递增子序列 - 力扣(LeetCode)

最长递增子序列:可以不连续但是前后相对顺序不变

def lengthOfLIS(nums):
    if not nums:
        return 0

    # 初始化一个数组 dp,长度与输入数组相同,所有元素初始为 1
    # dp[i] 表示以 nums[i] 结尾的最长递增子序列的长度
    dp = [1] * len(nums)

    # 遍历数组,计算每个位置的最长递增子序列长度
    for i in range(1, len(nums)):
        for j in range(i):
            # 如果当前元素 nums[i] 大于之前的元素 nums[j]
            # 并且以 nums[i] 结尾的子序列长度可以增加
            if nums[i] > nums[j]:
                dp[i] = max(dp[i], dp[j] + 1)

    # 返回 dp 数组中的最大值,即为最长递增子序列的长度
    return max(dp)

# 示例用法
nums = [10, 9, 2, 5, 3, 7, 101, 18]
print(lengthOfLIS(nums))  # 输出应该是 4,因为最长递增子序列是 [2, 3, 7, 101]

重点:如果 nums[i] 大于 nums[j],那么我们可以将 nums[i] 添加到以 nums[j] 结尾的递增子序列中,从而形成一个更长的递增子序列。 

152. 乘积最大子数组 - 力扣(LeetCode)

最大乘积子数组问题是指在一个整数数组中找到一个连续的子数组(至少包含一个数),使得这个子数组中所有数的乘积是最大的。

tmp = imax;
imax = imin;
imin = tmp;
def maxProduct(nums):
    # 初始化最大乘积、当前最大乘积和当前最小乘积
    max_so_far = nums[0]
    min_so_far = nums[0]
    result = nums[0]
    
    # 遍历数组,从第二个元素开始
    for i in range(1, len(nums)):
        # 如果当前元素是负数,那么最大乘积和最小乘积会交换
        if nums[i] < 0:
            max_so_far, min_so_far = min_so_far, max_so_far
        
        # 更新当前最大乘积和最小乘积
        max_so_far = max(nums[i], max_so_far * nums[i])
        min_so_far = min(nums[i], min_so_far * nums[i])
        
        # 更新结果为最大乘积
        result = max(result, max_so_far)
    
    return result

# 示例
nums = [2, 3, -2, 4]
print(maxProduct(nums))  # 输出: 6

416. 分割等和子集 - 力扣(LeetCode)

给定一个整数数组 ,如果您可以将数组划分为两个子集,使得两个子集中的元素之和相等,则 返回。

01背包问题

def canPartition(nums):
    total_sum = sum(nums)
    # 如果总和是奇数,直接返回 False
    if total_sum % 2 != 0:
        return False
    
    target = total_sum // 2
    dp = [False] * (target + 1)
    dp[0] = True  # 0 总是可以达成,不选择任何元素即可
    
    # 遍历所有物品(数组中的数字)
    for num in nums:
        # 这里必须从 target 递减到 num,以防止一个物品被重复使用
        for j in range(target, num - 1, -1):
            # 如果 dp[j - num] 是 True,则 dp[j] 也应该是 True
            dp[j] = dp[j] or dp[j - num]
    
    # dp[target] 表示是否可以从数组中选取一些数字,使得这些数字的总和等于 target
    return dp[target]

# 示例
nums = [1, 5, 11, 5]
print(canPartition(nums))  # 输出: True

32. 最长有效括号 - 力扣(LeetCode)

当前字符是 ) 并且前一个字符是 (,当前字符是 ) 并且前一个字符也是 ):s = "()(())"

def longest_valid_parentheses(s):
    n = len(s)
    if n == 0:
        return 0

    # dp[i] 表示以 s[i] 结尾的最长有效括号的长度
    dp = [0] * n
    max_len = 0

    for i in range(1, n):
        if s[i] == ')':
            # 如果前一个字符是'(',则可以形成一个有效的括号对
            if s[i - 1] == '(':
                dp[i] = (dp[i - 2] if i >= 2 else 0) + 2
            # 如果前一个字符是')',并且前面的有效括号长度为 dp[i - 1]
            # 并且 dp[i - 1] 前面的字符是'(',则可以扩展有效括号长度
            elif i - dp[i - 1] > 0 and s[i - dp[i - 1] - 1] == '(':
                dp[i] = dp[i - 1] + (dp[i - dp[i - 1] - 2] if i - dp[i - 1] >= 2 else 0) + 2
            max_len = max(max_len, dp[i])

    return max_len

# 示例
print(longest_valid_parentheses("(()"))  # 输出 2
print(longest_valid_parentheses(")()())"))  # 输出 4

多维动态规划

62. 不同路径 - 力扣(LeetCode)

与杨辉三角和爬楼梯类似

空间复杂度:2n

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        pre = [1] * n
        cur = [1] * n
        for i in range(1, m):
            for j in range(1, n):
                cur[j] = pre[j] + cur[j-1]
            pre = cur[:]
        return pre[-1]

64. 最小路径和 - 力扣(LeetCode)

向下或向右移动从左上角到右下角的最小路径和

到达每个单元格 (i, j) 的最小路径和可以由到达其上方单元格 (i-1, j) 和左方单元格 (i, j-1) 的最小路径和推导出来。

 dp[i][j] 表示到达单元格 (i, j) 的最小路径和,到达 (i, j) 的最小路径和等于到达其上方和左方单元格的最小路径和中的较小者,再加上当前单元格的值。

def min_path_sum(grid):
    if not grid or not grid[0]:
        return 0
    
    m, n = len(grid), len(grid[0])
    dp = [[0] * n for _ in range(m)]
    
    # 初始化左上角
    dp[0][0] = grid[0][0]
    
    # 初始化第一列
    for i in range(1, m):
        dp[i][0] = dp[i - 1][0] + grid[i][0]
    
    # 初始化第一行
    for j in range(1, n):
        dp[0][j] = dp[0][j - 1] + grid[0][j]
    
    # 填充dp数组剩余部分
    for i in range(1, m):
        for j in range(1, n):
            # dp[i][j]可以从上方(dp[i-1][j])或左方(dp[i][j-1])到达,
            # 所以我们取两者的最小值,并加上当前格子的值。
            dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
    
    # 右下角的格子将包含从左上角到右下角的最小路径和
    return dp[m - 1][n - 1]

5. 最长回文子串 - 力扣(LeetCode)

记录字符串中每对字符之间是否形成回文子串

def longest_palindromic_substring(s):
    n = len(s)
    if n == 0:
        return ""
    
    # 初始化一个二维数组,用于存储子串是否为回文
    dp = [[False] * n for _ in range(n)]
    start = 0  # 最长回文子串的起始位置
    max_length = 1  # 最长回文子串的长度

    # 所有长度为1的子串都是回文
    for i in range(n):
        dp[i][i] = True

    # 检查长度为2的子串是否为回文
    for i in range(n - 1):
        if s[i] == s[i + 1]:
            dp[i][i + 1] = True
            start = i
            max_length = 2

    # 检查长度大于2的子串
    for length in range(3, n + 1):  # 子串长度从3开始递增
        for i in range(n - length + 1):
            j = i + length - 1  # 子串的结束位置
            # 检查子串s[i:j+1]是否为回文
            if s[i] == s[j] and dp[i + 1][j - 1]:
                dp[i][j] = True
                start = i
                max_length = length

    # 返回最长回文子串
    return s[start:start + max_length]

# 示例使用
s = "babad"
print(longest_palindromic_substring(s))  # 输出可能是"bab"或"aba"

1143. 最长公共子序列 - 力扣(LeetCode)

子序列可以是不连续的;子数组(子字符串)需要是连续的。

def longest_common_subsequence(text1, text2):
    # 获取两个字符串的长度
    m, n = len(text1), len(text2)
    
    # 初始化一个二维数组 dp,用于存储子问题的解
    # dp[i][j] 表示 text1 的前 i 个字符和 text2 的前 j 个字符的最长公共子序列的长度
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    
    # 填充 dp 数组
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            # 如果当前字符相同,则最长公共子序列长度加一
            if text1[i - 1] == text2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                # 如果当前字符不同,取左上角、上方、左方三个方向的最大值
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
    
    # dp[m][n] 即为两个字符串的最长公共子序列的长度
    return dp[m][n]

# 测试用例
text1 = "abcde"
text2 = "ace"
# 测试结果
print(longest_common_subsequence(text1, text2))  

72. 编辑距离 - 力扣(LeetCode)

定两个字符串 和 ,返回将 word1 转换为 word2 所需的最小步数

def minDistance(word1, word2):
    # 创建一个矩阵来存储子问题的解
    dp = [[0 for x in range(len(word2) + 1)] for x in range(len(word1) + 1)]

    # 初始化矩阵的第一行和第一列
    for i in range(len(word1) + 1):
        dp[i][0] = i
    for j in range(len(word2) + 1):
        dp[0][j] = j

    # 填充dp矩阵
    for i in range(1, len(word1) + 1):
        for j in range(1, len(word2) + 1):
            # 如果当前字符相同,无需编辑,直接使用上一个状态的值
            if word1[i - 1] == word2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1]
            else:
                # 如果字符不同,取插入、删除、替换三者的最小值加1
                dp[i][j] = 1 + min(dp[i - 1][j],    # 删除
                                   dp[i][j - 1],    # 插入
                                   dp[i - 1][j - 1] # 替换
                                  )

    # dp矩阵的最后一个元素即为编辑距离
    return dp[len(word1)][len(word2)]

# 示例使用
word1 = "horse"
word2 = "ros"
print(minDistance(word1, word2))  # 输出应该是3,因为"horse" -> "ros"需要3步:h -> r, o ->

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2291195.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Rust自学】16.2. 使用消息传递来跨线程传递数据

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 16.2.1. 消息传递 有一种很流行而且能保证安全并发的技术&#xff08;或者叫机制&#xff09;叫做消息传递。在这种机制里&#xff0c;线…

解锁豆瓣高清海报(二) 使用 OpenCV 拼接和压缩

解锁豆瓣高清海报(二): 使用 OpenCV 拼接和压缩 脚本地址: 项目地址: Gazer PixelWeaver.py pixel_squeezer_cv2.py 前瞻 继上一篇“解锁豆瓣高清海报(一) 深度爬虫与requests进阶之路”成功爬取豆瓣电影海报之后&#xff0c;本文将介绍如何使用 OpenCV 对这些海报进行智…

利用腾讯云cloud studio云端免费部署deepseek-R1

1. cloud studio 1.1 cloud studio介绍 Cloud Studio&#xff08;云端 IDE&#xff09;是基于浏览器的集成式开发环境&#xff0c;为开发者提供了一个稳定的云端工作站。支持CPU与GPU的访问。用户在使用 Cloud Studio 时无需安装&#xff0c;随时随地打开浏览器即可使用。Clo…

DeepSeek r1本地安装全指南

环境基本要求 硬件配置 需要本地跑模型&#xff0c;兼顾质量、性能、速度以及满足日常开发需要&#xff0c;我们需要准备以下硬件&#xff1a; CPU&#xff1a;I9内存&#xff1a;128GB硬盘&#xff1a;3-4TB 最新SSD&#xff0c;C盘确保有400GB&#xff0c;其它都可划成D盘…

基于VMware的ubuntu与vscode建立ssh连接

1.首先安装openssh服务 sudo apt update sudo apt install openssh-server -y 2.启动并检查ssh服务状态 到这里可以按q退出 之后输入命令 &#xff1a; ip a 红色挡住的部分就是我们要的地址&#xff0c;这里就不展示了哈 3.配置vscode 打开vscode 搜索并安装&#xff1a;…

【LLM-agent】(task2)用llama-index搭建AI Agent

note LlamaIndex 实现 Agent 需要导入 ReActAgent 和 Function Tool&#xff0c;循环执行&#xff1a;推理、行动、观察、优化推理、重复进行。可以在 arize_phoenix 中看到 agent 的具体提示词&#xff0c;工具被装换成了提示词ReActAgent 使得业务自动向代码转换成为可能&am…

【Redis】Redis 经典面试题解析:深入理解 Redis 的核心概念与应用

Redis 是一个高性能的键值存储系统&#xff0c;广泛应用于缓存、消息队列、排行榜等场景。在面试中&#xff0c;Redis 是一个高频话题&#xff0c;尤其是其核心概念、数据结构、持久化机制和高可用性方案。 1. Redis 是什么&#xff1f;它的主要特点是什么&#xff1f; 答案&a…

FastExcel使用详解

文章目录 FastExcel使用详解一、引言二、环境准备与依赖引入1、Maven 依赖引入2、实体类定义 三、核心操作&#xff1a;读写 Excel1、读取 Excel1.1 自定义监听器1.2 读取文件 2、写入 Excel2.1 简单写入2.2 模板写入 四、Spring Boot 集成示例1、文件上传&#xff08;导入&…

图漾相机——C++语言属性设置

文章目录 前言1.SDK API功能介绍1.1 Device组件下的API测试1.1.1 相机工作模式设置&#xff08;TY_TRIGGER_PARAM_EX&#xff09;1.1.2 TY_INT_FRAME_PER_TRIGGER1.1.3 TY_INT_PACKET_DELAY1.1.4 TY_INT_PACKET_SIZE1.1.5 TY_BOOL_GVSP_RESEND1.1.6 TY_BOOL_TRIGGER_OUT_IO1.1.…

解决MacOS安装软件时提示“打不开xxx软件,因为Apple无法检查其是否包含恶意软件”的问题

macOS 系统中如何开启“任何来源”以解决安装报错问题&#xff1f; 大家好&#xff01;今天我们来聊聊在使用 macOS 系统 时&#xff0c;遇到安装应用软件时出现报错的情况。这种情况常常发生在安装一些来自第三方开发者的应用时&#xff0c;因为 macOS 会默认阻止不明开发者的…

实验十 Servlet(一)

实验十 Servlet(一) 【实验目的】 1&#xff0e;了解Servlet运行原理 2&#xff0e;掌握Servlet实现方式 【实验内容】 1、参考课堂例子&#xff0c;客户端通过login.jsp发出登录请求&#xff0c;请求提交到loginServlet处理。如果用户名和密码相同则视为登录成功&#xff0c…

MyBatis-Plus笔记-快速入门

大家在日常开发中应该能发现&#xff0c;单表的CRUD功能代码重复度很高&#xff0c;也没有什么难度。而这部分代码量往往比较大&#xff0c;开发起来比较费时。 因此&#xff0c;目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作。目前在国内使用较多的一个组件就是…

《超自然》:科学与灵性融合的自我转变之路

在现代社会中&#xff0c;许多人开始探寻自我成长、身心疗愈与灵性提升的可能性。Bestselling author Dr. Joe Dispenza 的《超自然&#xff1a;普通人如何创造非凡人生》正是在这样的大背景下问世的。书中既融合了量子物理、神经科学和表观遗传学的前沿理论&#xff0c;又吸收…

《Origin画百图》之脊线图

1.数据准备&#xff1a;将数据设置为y 2.选择绘图>统计图>脊线图 3.生成基础图形&#xff0c;并不好看&#xff0c;接下来对图形属性进行设置 4.双击图形>选择图案>颜色选择按点>Y值 5.这里发现颜色有色阶&#xff0c;过度并不平滑&#xff0c;需要对色阶进行更…

w189电商平台的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

让banner.txt可以自动读取项目版本

文章目录 1.sunrays-dependencies1.配置插件2.pluginManagement统一指定版本 2.common-log4j2-starter1.banner.txt使用$ 符号取出2.查看效果 1.sunrays-dependencies 1.配置插件 <!-- 为了让banner.txt自动获取版本号 --><plugin><groupId>org.apache.mave…

96,【4】 buuctf web [BJDCTF2020]EzPHP

进入靶场 查看源代码 GFXEIM3YFZYGQ4A 一看就是编码后的 1nD3x.php 访问 得到源代码 <?php // 高亮显示当前 PHP 文件的源代码&#xff0c;用于调试或展示代码结构 highlight_file(__FILE__); // 关闭所有 PHP 错误报告&#xff0c;防止错误信息泄露可能的安全漏洞 erro…

基于SpringBoot的智慧康老疗养院管理系统的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

音视频多媒体编解码器基础-codec

如果要从事编解码多媒体的工作&#xff0c;需要准备哪些更为基础的内容&#xff0c;这里帮你总结完。 因为数据类型不同所以编解码算法不同&#xff0c;分为图像、视频和音频三大类&#xff1b;因为流程不同&#xff0c;可以分为编码和解码两部分&#xff1b;因为编码器实现不…

Java线程认识和Object的一些方法ObjectMonitor

专栏系列文章地址&#xff1a;https://blog.csdn.net/qq_26437925/article/details/145290162 本文目标&#xff1a; 要对Java线程有整体了解&#xff0c;深入认识到里面的一些方法和Object对象方法的区别。认识到Java对象的ObjectMonitor&#xff0c;这有助于后面的Synchron…