算法 - 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;本文我们再次通过简短内容谈谈…

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

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

[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;小伙伴们说…

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

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

后端学习 - 基础 《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 线程的生命周期与状态二 线程间的…

打造跨平台.NET Core后台服务

续之前讲的在TopShelf上部署ASP.NET Core程序&#xff0c;作为后台服务运行&#xff0c;自从.NET Core 3.0出现以后&#xff0c;出现了自带的Generic Host&#xff0c;使得自托管服务变为可能。这种方式和TopShelf方式一样&#xff0c;可以直接F5进行服务的调试&#xff0c;也为…

iphone桌面横屏设置在哪里_我和我各司其职的桌面们

作者&#xff1a;旭彦兮沐桌面是只属于我们自己一个人的舞台&#xff0c;是与我们独处的好伙伴。好好布置一下自己的桌面&#xff0c;能在很大程度上保持我们心情的愉悦和做事情的效率&#xff0c;让我们保持专注当下的沉浸感。我最早了解到「桌面文化」其实是很早之前了&#…

后端学习 - RabbitMQ

文章目录一 MQ 的作用与基本概念1 流量削峰2 应用解耦3 异步调用4 四个基本概念二 核心模式1 工作队列模式&#xff08;Work Queue&#xff09;2 发布/订阅模式&#xff08;Publish / Subscribe&#xff09;3 路由模式&#xff08;Routing&#xff09;4 主题模式&#xff08;To…

dubbo k8s 服务发现_工商银行基于 Dubbo 构建金融微服务架构的实践-服务发现篇

简介&#xff1a; Dubbo 作为分布式微服务框架&#xff0c;众多公司在实践中基于 Dubbo 进行分布式系统架构。重启开源后&#xff0c;我们不仅看到 Dubbo 3.0 最新的 Roadmap 发布&#xff0c;而且还看到阿里在自身电商开始推进 Dubbo 和内部 HSF 的融合&#xff0c;并在 双11 …

初识ABP vNext(12):模块的独立运行与托管

点击上方蓝字"小黑在哪里"关注我吧模块运行动态 C# API 客户端前言很久没更新这个系列。。。之前的章节中讲到ABP的模块是可以独立运行的&#xff0c;但是没有介绍具体怎么操作&#xff0c;本篇就来讨论一下模块如何独立运行&#xff0c;以及一些托管方式。本人也是处…

后端学习 - Spring5

文章目录一 简介二 IOC1 底层原理2 实现过程3 Spring 实现 IOC 的两个接口二 Bean1 普通 Bean 与 FactoryBean2 Bean 单例与否的设置3 Bean 的生命周期三 IOC 的 Bean 管理&#xff08;XML&#xff09;1 创建对象2 属性注入 - 使用 set 方法3 属性注入 - 通过有参构造器实现3 注…