算法 - DFS/BFS

写DFS函数的时候首先确定当前位置是否已经加入路径

DFS函数大概率会传递“位置信息”,根据位置信息获取下一步的选择,(大部分是在循环中)选择、执行、回退

在哪做选择,就在哪退出选择,参考题9

def DFS():# 出口if 满足出口条件:将满足条件的路径加入结果方法返回for 选择 in 选择列表:# 做选择将该选择从选择列表移除路径.add(选择)DFS(路径, 选择列表)# 撤销选择路径.remove(选择)将该选择再加入选择列表

DFS和BFS本质上是决策树的遍历
动态规划要求重叠子问题的条件,而DFS/BFS则是没有任何条件的纯暴力搜索。而且,BFS找到的路径一定是最短的,但代价通常比DFS要大得多。

DFS通常的问题形式是,找各种意义的路径:棋盘摆放、集合划分等等。
BFS通常的问题形式是,寻找(决策树上的)最短路径。

画出递归树,想想每个节点该怎么做

文章目录

  • ==写DFS函数的时候首先确定当前位置是否已经加入路径==
  • 1.DFS - N皇后问题
  • 2.DFS - 子集(幂集)
  • 3.DFS - 组合
  • 4.DFS - 括号生成
  • 5.BFS - 二叉树的最小深度
  • 6.BFS - 转盘锁
  • 7.DFS - 组合总数
  • 8.字符串的全排列
  • 9.是否存在word路径
  • 10.最长递增路径
  • 11.循环依赖

1.DFS - N皇后问题

  • 每个格子都面临选择:放或者不放皇后
  • 注意一个优化点:如果某行放了皇后,则下次递归直接跳到下行
class Solution(object):res = []def solveNQueens(self, n):""":type n: int:rtype: List[List[str]]"""self.res = []path = [["." for _ in range(n)] for _ in range(n)]self.dfs(path, 0, 0, n)return self.resdef dfs(self, path, x, y, queens_left):n = len(path)# 出口if queens_left == 0:path_strs = []for i in range(n):path_strs.append("".join(path[i]))self.res.append(path_strs)returnelif x > n - 1 or y > n - 1:return# 一般情况# 选择1:放皇后is_queen_available = Truefor k in range(n):  # 检查行和列is_queen_available = False if path[x][k] == "Q" else is_queen_availableis_queen_available = False if path[k][y] == "Q" else is_queen_availabletemp_x, temp_y = x - 1, y - 1  # 检查左上方和右上方while (temp_x >= 0 and temp_y >= 0):if path[temp_x][temp_y] == "Q":is_queen_available = Falsetemp_x -= 1temp_y -= 1temp_x, temp_y = x - 1, y + 1while (temp_x >= 0 and temp_y <= n - 1):if path[temp_x][temp_y] == "Q":is_queen_available = Falsetemp_x -= 1temp_y += 1if is_queen_available:path[x][y] = "Q"  # 做选择queens_left -= 1self.dfs(path, x + 1, 0, queens_left)path[x][y] = "."  # 撤销选择queens_left += 1# 选择2:不放皇后if y == n - 1:x_new = x + 1y_new = 0else:x_new = xy_new = y + 1self.dfs(path, x_new, y_new, queens_left)

2.DFS - 子集(幂集)

  • 每次递归都面临问题:是否将下标 idx 的数字加入结果集
  • 撤销选择使用 path.pop() 而不能使用 path = path[: -1]
class Solution(object):res = []def subsets(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""self.res = [[]]def dfs(path, nums, idx):# 出口if idx > len(nums) - 1:return# 做选择:idx放入结果path.append(nums[idx])self.res.append(path[:])dfs(path, nums, idx + 1)path.pop()  # 撤销选择,不能使用 path = path[: -1]# 做选择:idx不放入结果dfs(path, nums, idx + 1)dfs([], nums, 0)return self.res

3.DFS - 组合

  • 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合
  • 没有思路时画出树结构帮助分析
    在这里插入图片描述
class Solution(object):res = []path = []def combine(self, n, k):""":type n: int:type k: int:rtype: List[List[int]]"""self.res = []self.path = []def dfs(n, k, idx):if len(self.path) == k:self.res.append(self.path[:])return# 站在当前的位置:path已有一些值,则要遍历idx及之后的所有选项for i in range(idx, n + 1):self.path.append(i)dfs(n, k, i + 1)self.path.pop()dfs(n, k, 1)return self.res

4.DFS - 括号生成

  • 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
  • 有效括号组合需满足:左括号必须以正确的顺序闭合。
class Solution(object):res = []path = []def generateParenthesis(self, n):""":type n: int:rtype: List[str]"""self.res = []self.path = []def dfs(n, l, r):# 出口if r == n:self.res.append("".join(self.path))return# 做选择# 1.左右括号相等,只能添加左括号if l == r:self.path.append("(")dfs(n, l + 1, r)self.path.pop()# 2.左括号大于右括号,且l<n,能添加左也能添加右else:if l < n:  # 可以加左括号self.path.append("(")dfs(n, l + 1, r)self.path.pop()self.path.append(")")dfs(n, l, r + 1)self.path.pop()dfs(n, 0, 0)return self.res###########################################################################################
"""用字符串记录path,没有回溯"""
class Solution(object):res = []def generateParenthesis(self, n):""":type n: int:rtype: List[str]"""self.res = []self.find(n, "", 0, 0)return self.resdef find(self, n, s, left, right):# 出口if left == n and right == n:self.res.append(s)# 做出所有的选择# 如果左括号不大于右括号,只能添加左括号elif left <= right:self.find(n, s + "(", left + 1, right)# 可以添加右括号,也能添加左括号(如果还能添加)else:if left != n:self.find(n, s + "(", left + 1, right)self.find(n, s + ")", left, right + 1)

5.BFS - 二叉树的最小深度

找好搜索的结束条件,当一个节点没有左右子节点时,到达叶节点。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):def minDepth(self, root):""":type root: TreeNode:rtype: int"""if root is None:return 0queue = [root]depth = 1while (len(queue) > 0):length = len(queue)for i in range(length):temp_node = queue[i]# 结束条件if temp_node.left is None and temp_node.right is None:return depthif temp_node.left is not None:queue.append(temp_node.left)if temp_node.right is not None:queue.append(temp_node.right)queue = queue[length:]depth += 1

6.BFS - 转盘锁

class Solution(object):def openLock(self, deadends, target):""":type deadends: List[str]:type target: str:rtype: int"""# 工具方法def plus(num, bit):origin = int(num[bit])if origin < 9:origin += 1else:origin = 0if bit == 0:return str(origin) + num[1: ]elif bit == 3:return num[0: 3] + str(origin)else:return num[0: bit] + str(origin) + num[bit + 1: ]def minus(num, bit):origin = int(num[bit])if origin > 0:origin -= 1else:origin = 9if bit == 0:return str(origin) + num[1: ]elif bit == 3:return num[0: 3] + str(origin)else:return num[0: bit] + str(origin) + num[bit + 1: ]# 特判if "0000" in deadends:return -1# BFSadded = ["0000"]  # 不走回头路,重要!queue = ["0000"]times = 0while (len(queue) > 0):length = len(queue)for i in range(length):code = queue[i]# 出口if code == target:return times# 处理每一位for j in range(4):temp_code = plus(code, j)  # +1if temp_code not in deadends and temp_code not in added:queue.append(temp_code)added.append(temp_code)temp_code = minus(code, j)  # -1if temp_code not in deadends and temp_code not in added:queue.append(temp_code)added.append(temp_code)times += 1queue = queue[length:]return -1

以上为经典的BFS解法。对于已知终点的BFS问题,可以采用双向BFS加速运行,从起点和终点向中间扩展,直到出现交集。

int openLock(String[] deadends, String target) {Set<String> deads = new HashSet<>();for (String s : deadends) deads.add(s);// 用集合不用队列,可以快速判断元素是否存在Set<String> q1 = new HashSet<>();Set<String> q2 = new HashSet<>();Set<String> visited = new HashSet<>();int step = 0;q1.add("0000");q2.add(target);while (!q1.isEmpty() && !q2.isEmpty()) {// 哈希集合在遍历的过程中不能修改,用 temp 存储扩散结果Set<String> temp = new HashSet<>();/* 将 q1 中的所有节点向周围扩散 */for (String cur : q1) {/* 判断是否到达终点 */if (deads.contains(cur))continue;if (q2.contains(cur))return step;visited.add(cur);/* 将一个节点的未遍历相邻节点加入集合 */for (int j = 0; j < 4; j++) {String up = plusOne(cur, j);if (!visited.contains(up))temp.add(up);String down = minusOne(cur, j);if (!visited.contains(down))temp.add(down);}}/* 在这里增加步数 */step++;// temp 相当于 q1// 这里交换 q1 q2,下一轮 while 就是扩散 q2q1 = q2;q2 = temp;}return -1;
}

7.DFS - 组合总数

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

  • 如何保证同一元素可被无限选取?
class Solution(object):res = []path = []def combinationSum(self, candidates, target):""":type candidates: List[int]:type target: int:rtype: List[List[int]]"""self.res = []self.path = []def dfs(candidates, target, now_sum, idx):# 出口if now_sum == target and self.path not in self.res:self.res.append(self.path[:])returnelif now_sum > target:return# 做选择for i in range(idx, len(candidates)):self.path.append(candidates[i])now_sum += candidates[i]dfs(candidates, target, now_sum, i)  # 此步是保证同一数字无限选择的关键,《i从idx开始》now_sum -= candidates[i]self.path.pop()dfs(candidates, target, 0, 0)return self.res

8.字符串的全排列

  • 标准的模板,参考文章开头
class Solution(object):def permutation(self, s):""":type s: str:rtype: List[str]"""# 一个全排列问题# 用DFS"""在DFS做选择/撤销时,加入path!!!!!!!!!!!!!!!!!!!!!!!!"""length = len(s)path = []visited = [0 for _ in range(length)]res = []def dfs(s, length):# 递归出口if sum(visited) == length:resstr = "".join(path)if resstr not in res:res.append(resstr)return # 做选择+撤销选择for i in range(length):if visited[i] == 0:visited[i] = 1path.append(s[i])dfs(s, length)path.pop()visited[i] = 0dfs(s, length)return res

9.是否存在word路径

  • 不能在注释处“撤销选择”,因为“做选择”是在dfs开始是进行的,不能从子选项中回退
class Solution:def hasPath(self , matrix , word ):def dfs(x, y, m, n, pos):visited[x][y] = True  # 在这个层面上做的选择,不能从子选项中回退path.append(matrix[x][y])if pos == len(word) - 1:return True# 做选择+撤销选择res = Falselx, ly = x, y - 1if ly >= 0 and not visited[lx][ly] and matrix[lx][ly] == word[pos + 1]:res = res or dfs(lx, ly, m, n, pos + 1)
#                 visited[lx][ly] = False
#                 path.pop()rx, ry = x, y + 1if ry < n and not visited[rx][ry] and matrix[rx][ry] == word[pos + 1]:res = res or dfs(rx, ry, m, n, pos + 1)
#                 visited[rx][ry] = False
#                 path.pop()ux, uy = x - 1, yif ux >= 0 and not visited[ux][uy] and matrix[ux][uy] == word[pos + 1]:res = res or dfs(ux, uy, m, n, pos + 1)
#                 visited[ux][uy] = False
#                 path.pop()dx, dy = x + 1, yif dx < m and not visited[dx][dy] and matrix[dx][dy] == word[pos + 1]:res = res or dfs(dx, dy, m, n, pos + 1)
#                 visited[dx][dy] = False
#                 path.pop()visited[x][y] = False  # 应该在这里撤销选择path.pop()return resm = len(matrix)if m == 0:return Falsen = len(matrix[0])if n == 0:return Falsevisited = [[False for _ in range(n)] for _ in range(m)]path = []for i in range(m):for j in range(n):if matrix[i][j] == word[0]:res = dfs(i, j, m, n, 0)if res:return Truereturn False
  • 如果想在各个选项中“选择”并“撤销”,可以采用下面的写法,但初始条件要修改
class Solution:def hasPath(self , matrix , word ):def dfs(x, y, m, n, pos):if pos == len(word) - 1:return True # 做选择+撤销选择res = False lx, ly = x, y - 1if ly >= 0 and not visited[lx][ly] and matrix[lx][ly] == word[pos + 1]:visited[lx][ly] = True path.append(word[pos + 1])res = res or dfs(lx, ly, m, n, pos + 1)visited[lx][ly] = False path.pop()rx, ry = x, y + 1if ry < n and not visited[rx][ry] and matrix[rx][ry] == word[pos + 1]:visited[rx][ry] = True path.append(word[pos + 1])res = res or dfs(rx, ry, m, n, pos + 1)visited[rx][ry] = False path.pop()ux, uy = x - 1, y if ux >= 0 and not visited[ux][uy] and matrix[ux][uy] == word[pos + 1]:visited[ux][uy] = True path.append(word[pos + 1])res = res or dfs(ux, uy, m, n, pos + 1)visited[ux][uy] = False path.pop()dx, dy = x + 1, y if dx < m and not visited[dx][dy] and matrix[dx][dy] == word[pos + 1]:visited[dx][dy] = True path.append(word[pos + 1])res = res or dfs(dx, dy, m, n, pos + 1)visited[dx][dy] = False path.pop() return res m = len(matrix)if m == 0:return False n = len(matrix[0])if n == 0:return False visited = [[False for _ in range(n)] for _ in range(m)]for i in range(m):for j in range(n):if matrix[i][j] == word[0]:path = [word[0]]visited[i][j] = Trueres = dfs(i, j, m, n, 0) if res:return True visited[i][j] = False return False

10.最长递增路径

给定一个 n 行 m 列矩阵 matrix ,矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径,使这条路径上的元素是递增的。并输出这条最长路径的长度

  • 如果用纯DFS会超时
  • 发现重叠子问题:记录以每个位置开始的最长路径长度,如果邻接的格子比当前更大,则一定没走过
class Solution:def solve(self , matrix ):# write code herem, n = len(matrix), len(matrix[0])# visited = [[False for _ in range(n)] for _ in range(m)]# 改良版dp = [[0 for _ in range(n)] for _ in range(m)]def dfs(x, y, m, n):if dp[x][y] > 0:return dp[x][y]u, d, l, r = 0, 0, 0, 0ux, uy = x - 1, ydx, dy = x + 1, ylx, ly = x, y - 1rx, ry = x, y + 1# 如果邻接的格子比当前更大,则一定没走过if ux >= 0 and matrix[ux][uy] > matrix[x][y]:u = dfs(ux, uy, m, n)if dx < m and matrix[dx][dy] > matrix[x][y]:d = dfs(dx, dy, m, n)if ly >= 0 and matrix[lx][ly] > matrix[x][y]:l = dfs(lx, ly, m, n)if ry < n and matrix[rx][ry] > matrix[x][y]:r = dfs(rx, ry, m, n)dp[x][y] = 1 + max(l, d, u, r)return dp[x][y]res = 1for i in range(m):for j in range(n):res = max(dfs(i, j, m, n), res)return res

11.循环依赖

class P3:def __init__(self):self.res = []self.path = []def find(self, serivces):num = len(serivces)visited = [False for _ in range(num)]def dfs(idx, num):# idx已经加入!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!(n0, n1) = serivces[idx]# 检查是否有循环依赖for t in self.path[: -1]:  # 因为最后一个元素是新加入的所以不能用来检测成环!!!!!!!!!!!!!!!!!!!!if t[0] == n1 or t[1] == n1:self.res.append(self.path[:])return None# 继续进行for i in range(num):if not visited[i] and serivces[i][0] == n1:self.path.append(serivces[i])visited[i] = Truedfs(i, num)self.path.pop()visited[i] = Falsefor i in range(num):self.path.append(serivces[i])visited[i] = Truedfs(i, num)self.path.pop()visited[i] = Falsereturn self.resprint(P3().find([('A', 'B'), ('B', 'C'), ('C', 'A'), ('A', 'D')]))

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

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

相关文章

你想象中的Task后续,很简单?

【导读】前不久&#xff0c;写过一篇关于Task的简短文章&#xff0c;通过评论和转载得到好评&#xff0c;刚好我昨晚又写了一篇实现简单的消息队列也提到了Task&#xff0c;难道不应该是看具体执行什么操作&#xff0c;再考虑最佳方案&#xff1f;本文我们再次通过简短内容谈谈…

算法 - 动态规划

动态规划是一种自底向上的算法&#xff0c;通常用于解决最大、最小等最值问题。 能使用动态规划解决的问题&#xff0c;一定具备&#xff1a; 重叠子问题&#xff1a;和暴力搜索不同&#xff0c;需要记录子问题的解&#xff0c;避免重复求解&#xff08;剪枝&#xff09;最优…

5G在工业互联网应用的机遇与挑战

移动通讯经过十年一代的发展&#xff0c;已经从1G发展到了5G&#xff0c;峰值速率实现十年千倍的增长&#xff0c;1G到4G是面向个人的&#xff0c;而5G是面向产业互联网和智慧城市服务。5G是一个颠覆性的技术&#xff0c;低时延&#xff08;每秒钟下载一部高清电影&#xff09;…

算法 - 前缀和

记录在做hot100时遇到的前缀和的题目。 目前见过的题目&#xff0c;都是前缀和结合其它的方法一起使用&#xff1a;用于求取一段连续路径的和&#xff08;最大值/最小值/目标出现次数&#xff09;。 需要注意的是&#xff0c;前缀和的判定方法是node2.val-node1.val target&am…

[C#.NET 拾遗补漏]10:理解 volatile 关键字

要理解 C# 中的 volatile 关键字&#xff0c;就要先知道编译器背后的一个基本优化原理。比如对于下面这段代码&#xff1a;public class Example {public int x;public void DoWork(){x 5;var y x 10;Debug.WriteLine("x " x ", y " y);} }在 Releas…

跟我一起学.NetCore之MediatR好像有点火

前言随着微服务的流行&#xff0c;而DDD(领域驱动设计)也光速般兴起&#xff0c;CRQS(Command Query Responsibility Seperation--命令查询职责分离)、领域事件名词是不是经常在耳边环绕&#xff0c;而MediatR组件经常用来对其技术的落地&#xff0c;凭这&#xff0c;小伙伴们说…

数据结构 - 单调栈、单调队列

单调栈&#xff1a;每日温度 请根据每日 气温 列表 temperatures &#xff0c;请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替单调栈基本只处理NGE问题&#xff08;Next GreaterElement&#xff09;。对序列中每个元…

不想写脚本清理 mongodb 中的垃圾数据,ttlIndex 能帮到你!

mongodb一直都在不断的更新&#xff0c;不断的发展&#xff0c;那些非常好玩也非常实用的功能都逐步加入到了mongodb中&#xff0c;这不就有了本篇对ttlindex的介绍&#xff0c;刚好我们的生产业务场景中就有一个案例。。。一&#xff1a;案例分析 生产的推荐系统要给用户发送短…

数据结构 - 最小堆最大堆

可以在O(nlogn)的时间复杂度内完成排序典型的用法是&#xff0c;寻找 第k个/前k个 最大/最小元素&#xff0c;k个有序序列合并 1.合并K个升序链表&#xff08;最小堆实现&#xff09; 或许可以改进成每次堆只存放K个元素&#xff1f; # Definition for singly-linked list. …

python程序启动其他python程序,如何使用Python启动应用程序的实例?

I am creating a Python script where it does a bunch of tasks and one of those tasks is to launch and open an instance of Excel. What is the ideal way of accomplishing that in my script?解决方案While the Popen answers are reasonable for the general case, I…

工作这几年所获、所感、所悟

【导读】截止到目前&#xff0c;给甲方所做项目已接近尾声&#xff0c;在此写下一点个人关于技术方面的感受。若后续时间上允许或充裕的话&#xff0c;打算私下花一点时间分享封装可通用的组件今年也是我首次带小伙伴&#xff0c;有刚毕业没什么技术经验&#xff0c;也有毕业不…

后端学习 - 基础 《Java编程的逻辑》读书笔记

文章目录一 基础概念1 有关Java2 JVM / JDK / JRE3 与C的联系和区别4 各类型数据占用空间大小5 和 equals() 的区别、hashCode() 方法6 包装类型7 final 关键字8 参数传递机制&#xff1a;值传递9 String 的内存情况10 访问修饰符11 引用拷贝、浅拷贝与深拷贝三 面向对象1 面向…

cheatengine找不到数值_彩票中奖500万,领了还不到一半?这些问题不解决,钱都拿不走...

长期以来&#xff0c;“一夜暴富”是很多人梦寐以求的梦想&#xff0c;而作为最能让人“一夜暴富”的方式要数我国的福利彩票了&#xff0c;这也是很多人最容易活动暴富的机会&#xff0c;不少彩民长久以来一直买彩票的梦想就是“一夜暴富”。而突然暴富是很多人的梦想&#xf…

一站式Web开发套件BeetleX.WebFamily

BeetleX.WebFamily是一款前后端分离的Web开发套件&#xff0c;但它并不依赖于nodejs/npm/webpack等相关工具&#xff1b;而使用自身实现的方式来完成前后端分离的Web应用开发&#xff1b;套件以组件的方式发布&#xff0c;只需要在项目引用相关组件即可实现前后端分离开发&…

.NET架构小技巧(2)——访问修饰符正确姿势

在C#中&#xff0c;访问修饰符是使用频率很高的一组关键字&#xff0c;一共四个单词六个组合&#xff1a;public,internal,protected internal,protected,private protected,private&#xff0c;如果你对这些关键字非常清楚&#xff0c;请跳过&#xff0c;节省时间&#xff1b;…

能源36号文解读_IDC报告预测:今年中国新能源汽车销量将达116万辆,未来五年复合增长率36%_详细解读_最新资讯_热点事件...

编者按&#xff1a;本文来自36氪「 未来汽车日报」&#xff0c;(微信公众号ID&#xff1a;auto-time)&#xff0c;作者&#xff1a;秦章勇。 来源&#xff1a;IDC作者 | 秦章勇编辑 | 周游12月3日&#xff0c;在2020世界智能汽车大会上&#xff0c;IDG亚洲(国际数据(亚洲)集团)…

后端学习 - 容器

文章目录一 简介二 底层数据结构总结1 List2 Set3 Queue4 Map三 Collection 的子接口 List1 ArrayList 与 Vector2 ArrayList 与 LinkedList3 ArrayList 的 JDK 7/8 差异4 ArrayList 的构造方法与扩容机制*四 Collection 的子接口 Set1 HashSet、LinkedHashSet 和 TreeSet2 Has…

简单聊聊AspNetCore的启动流程

首先&#xff0c;得和大家达成一个共识&#xff0c;即AspNetCore项目其实就是一个控制台项目。可以简单的理解&#xff0c;AspNetCore就是将一个Web服务器宿主在一个进程(即控制台)中&#xff0c;然后它在这个进程中进行http请求的监听处理。AspNetCore中默认使用kestrel作为we…

共聚焦图片怎么加标尺_聚焦扶贫政策,打造小康生活

导语&#xff1a;农村独栋小楼、整洁的水泥路……扶贫产业蓬勃发展&#xff0c;我省结合实际&#xff0c;狠抓特色产业&#xff0c;助力脱贫攻坚&#xff0c;实现乡村振兴。武宁县&#xff1a;“四个聚焦”巩固脱贫成果2020年是全面建成小康社会目标实现之年&#xff0c;是全面…

后端学习 - 并发编程

文章目录零 基本概念1 CAS、ABA 问题和原子变量2 this 引用逸出3 不变性 immutable4 同步、异步、阻塞、非阻塞5 JMM6 同步方案演示&#xff1a;计数器 demo*一 进程与线程1 区别与联系2 Java内存区域3 线程组4 线程的上下文切换5 并发与并行6 线程的生命周期与状态二 线程间的…