自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(82)
  • 收藏
  • 关注

原创 如何保证缓存与数据库的数据一致性

保证是分布式系统中最经典、最棘手的问题之一。没有“绝对强一致”的缓存方案,但我们可以根据业务场景选择的最优平衡。下面从四个维度,系统性地讲解如何保障缓存与数据库的一致性。

2025-08-18 21:47:47 1190

原创 并发编程中的ReentrantLock是什么

是一个显式的、可重入的、互斥的锁,用于保证在同一时刻只有一个线程可以访问某个临界资源。Reentrant(可重入):同一个线程可以多次获取同一个锁。Lock(锁):用于实现线程间的互斥访问。显式锁(Explicit Lock):需要程序员手动调用lock()和unlock()方法。特性说明可重入同一线程可多次获取锁显式控制手动加锁/解锁,更灵活公平/非公平可选择调度策略可中断/超时避免无限等待Condition支持多条件等待💡一句话总结是的“增强版”,适合复杂并发场景;而更适合简单、安全的同步需求。

2025-08-16 20:57:43 980

原创 并发编程中,可重入锁是什么

可重入同一个线程可以多次获取同一个锁,而不会造成死锁或阻塞。也就是说,一个线程在已经持有某个锁的情况下,再次请求这个锁时,仍然可以成功获取,而不是被自己挡在外面。一句话可重入 = 同一个线程可以多次获取同一个锁,不会把自己锁在外面。✅和都是可重入的。❌ 不可重入的锁会导致自锁或死锁。🔑 可重入是 Java 并发安全的重要保障,让锁的使用更安全、灵活。“可重入”是为了让线程“自己不挡自己的路”。考虑有没有可能发生这种情况,可重入锁要是b方法中的锁被其他线程持有了,会怎么样问题回答b()

2025-08-16 17:34:42 772

原创 并发编程中,线程饥饿什么意思,线程池又是如何引发线饥饿的

线程饥饿 = 长期得不到资源 → 任务无法执行常见于:非公平锁、高优先级线程、资源竞争激烈、线程池不合理。不像死锁那样“死掉”,而是“活不好”。保证资源分配的公平性 + 合理设计并发模型。💡 在实际开发中,要平衡“性能”和“公平”。大多数情况下,非公平锁 + 合理的任务设计,比完全公平更高效。线程池在设计或使用不当时,确实可能引发线程饥饿(Thread Starvation),即某些任务长期得不到执行机会,一直处于等待状态。下面我们来详细分析线程池是如何引发线程饥饿的。

2025-08-16 17:24:07 816

原创 并发编程中公平锁,非公平锁有什么区别

公平锁会严格按照线程请求锁的先后顺序来分配锁。也就是说,先调用lock()的线程会优先获得锁(遵循 FIFO,先进先出)。非公平锁不保证线程获取锁的顺序。即使有线程在排队,刚释放锁时,如果有新线程来,它可能直接抢到锁。特性公平锁非公平锁(默认)构造方式或false获取顺序严格 FIFO(先来先得)不保证顺序,允许插队性能较低(频繁上下文切换)较高(减少等待)吞吐量低高 ✅是否可能饥饿基本不会可能(但实际很少)使用场景对顺序敏感、调试、特殊需求大多数情况推荐使用一句话概括。

2025-08-16 17:07:10 830

原创 LeetCode 热题 100 -- 79. 单词搜索

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCB”输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “SEE”

2025-08-12 17:49:54 399

原创 LeetCode 热题 100 -- 22. 括号生成

输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。通过递归生成所有可能的括号组合,在每一步保证括号的有效性。

2025-08-12 17:05:54 274

原创 LeetCode 热题 100 -- 39. 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。2 和 3 可以形成一组候选,2 + 2 + 3 = 7。输入:candidates = [2,3,6,7], target = 7。输入: candidates = [2,3,5], target = 8。输出: [[2,2,2,2],[2,3,3],[3,5]]输出:[[2,2,3],[7]]

2025-08-12 16:48:09 549

原创 LeetCode 热题 100 -- 17. 电话号码的字母组合

输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”]给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。使用回溯算法递归生成所有可能的字母组合,通过逐步构建字符串并回溯来探索所有可能性。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。digits[i] 是范围 [‘2’, ‘9’] 的一个数字。输入:digits = “23”输出:[“a”,“b”,“c”]输入:digits = “2”输入:digits = “”

2025-08-12 16:20:29 153

原创 LeetCode 热题 100 -- 78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同。返回该数组所有可能的子集(幂集)。输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]使用回溯算法递归生成所有子集,通过逐步构建子集并回溯来避免重复。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。输入:nums = [1,2,3]nums 中的所有元素 互不相同。输入:nums = [0]输出:[[],[0]]

2025-08-12 15:53:05 233

原创 LeetCode 热题 100 -- 46. 全排列

输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列。你可以 按任意顺序 返回答案。使用回溯算法递归生成所有可能的排列,通过交换元素和回溯来避免重复计算。输入:nums = [1,2,3]输出:[[0,1],[1,0]]nums 中的所有整数 互不相同。输入:nums = [0,1]输入:nums = [1]

2025-08-12 15:41:34 115

原创 LeetCode 热题 100 -- 208. 实现 Trie (前缀树)

boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true;Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);

2025-08-12 14:39:01 558

原创 LeetCode 热题 100 -- 207. 课程表

先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。输入:numCourses = 2, prerequisites = [[1,0],[0,1]]例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1。学习课程 1 之前,你需要完成课程 0。邻接表,入度数组,队列。

2025-08-12 14:02:41 283

原创 LeetCode 热题 100 -- 994. 腐烂的橘子

解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个方向上。使用广度优先搜索(BFS)从所有腐烂的橘子同时开始扩散,记录每个橘子被腐烂的时间。输入:grid = [[2,1,1],[1,1,0],[0,1,1]]输入:grid = [[2,1,1],[0,1,1],[1,0,1]]每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0。输入:grid = [[0,2]]值 2 代表腐烂的橘子。

2025-08-12 06:36:59 202

原创 LeetCode 热题 100 -- 200. 岛屿数量

采用广度优先搜索(BFS)遍历网格,遇到 ‘1’ 时进行标记并递归/迭代搜索相邻的 ‘1’。给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。grid[i][j] 的值为 ‘0’ 或 ‘1’此外,你可以假设该网格的四条边均被水包围。输入:grid = [输入:grid = [

2025-08-12 06:13:16 259

原创 力扣 LeetCode -- 912. 排序数组 (快速排序)

解释:数组排序后,某些数字的位置没有改变(例如,2 和 3),而其他数字的位置发生了改变(例如,1 和 5)。你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为 O(nlog(n)),并且空间复杂度尽可能小。i是比主元小的元素中最右边的那个元素的下标,也就是最右边界。给你一个整数数组 nums,请你将该数组升序排列。输入:nums = [5,1,1,2,0,0]解释:请注意,nums 的值不一定唯一。输入:nums = [5,2,3,1]输出:[0,0,1,1,2,5]输出:[1,2,3,5]

2025-08-11 18:15:31 305

原创 LeetCode 热题 100 -- 124. 二叉树中的最大路径和

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。该路径 至少包含一个 节点,且不一定经过根节点。解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42。解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6。输入:root = [-10,9,20,null,null,15,7]给你一个二叉树的根节点 root ,返回其 最大路径和。输入:root = [1,2,3]路径和 是路径中各节点值的总和。

2025-08-11 17:17:26 191

原创 LeetCode 热题 100 -- 236. 二叉树的最近公共祖先

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1。输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4。1.​​祖先节点​​:若节点 x在从根节点到节点 y的路径上,则 x是 y的祖先。

2025-08-11 16:42:52 229

原创 LeetCode 热题 100 -- 437. 路径总和 III

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22。输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8。路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

2025-08-11 16:12:08 131

原创 LeetCode 热题 100 -- 105. 从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]输入: preorder = [-1], inorder = [-1]输出: [3,9,20,null,null,15,7]preorder 和 inorder 均 无重复 元素。inorder 均出现在 preorder。

2025-08-11 15:45:40 197

原创 LeetCode 热题 100 -- 114. 二叉树展开为链表

展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null。输出:[1,null,2,null,3,null,4,null,5,null,6]采用变形的后序遍历(右→左→根),在遍历过程中修改指针指向。输入:root = [1,2,5,3,4,null,6]展开后的单链表应该与二叉树 先序遍历 顺序相同。树中结点数在范围 [0, 2000] 内。输入:root = [0]输入:root = []

2025-08-11 11:14:14 132

原创 LeetCode 热题 100 -- 199. 二叉树的右视图

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。输入:root = [1,2,3,4,null,null,null,5]输入:root = [1,2,3,null,5,null,4]使用队列进行层序遍历,记录每层的最后一个节点值。输入:root = [1,null,3]二叉树的节点个数的范围是 [0,100]输出:[1,3,4,5]输入:root = []输出:[1,3,4]

2025-08-11 11:01:35 254

原创 LeetCode 热题 100 -- 230. 二叉搜索树中第 K 小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。输入:root = [5,3,6,2,4,null,null,1], k = 3。使用栈实现中序遍历,可以在找到第 k 个元素时立即返回,避免完整遍历。输入:root = [3,1,4,null,2], k = 1。树中的节点数为 n。

2025-08-11 10:48:17 180

原创 LeetCode 热题 100 -- 98. 验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。输入:root = [5,1,4,null,null,3,6]解释:根节点的值是 5 ,但是右子节点的值是 4。节点的左子树只包含 严格小于 当前节点的数。节点的右子树只包含 严格大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。树中节点数目范围在[1, 104] 内。输入:root = [2,1,3]

2025-08-11 10:31:40 231

原创 LeetCode 热题 100 -- 108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。1.​​BST性质​​:左子树 < 根 < 右子树。输入:nums = [-10,-3,0,5,9]输出:[0,-3,9,-10,null,5]​​2.平衡要求​​:选择中间元素作为根节点。nums 按 严格递增 顺序排列。输入:nums = [1,3]采用二分法递归构建平衡BST。

2025-08-11 10:15:09 101

原创 LeetCode 热题 100 -- 102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历。(即逐层地,从左到右访问所有节点)。输入:root = [3,9,20,null,null,15,7]输出:[[3],[9,20],[15,7]]使用队列进行广度优先搜索,记录每层的节点数。树中节点数目在范围 [0, 2000] 内。输入:root = [1]输入:root = []

2025-08-10 17:10:20 493

原创 LeetCode 热题 100 -- 543. 二叉树的直径

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度。3.​​返回深度​​:当前节点的深度为 max(左子树深度, 右子树深度) + 1。​​2.更新直径​​:当前节点的直径候选值为 左子树深度 + 右子树深度。解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。1.​​递归计算深度​​:对于每个节点,计算其左右子树的深度。给你一棵二叉树的根节点,返回该树的 直径。输入:root = [1,2,3,4,5]输入:root = [1,2](递归栈的深度,h为树的高度)

2025-08-10 16:59:16 112

原创 LeetCode 热题 100 -- 101. 对称二叉树

输入:root = [1,2,2,null,3,null,3]给你一个二叉树的根节点 root , 检查它是否轴对称。输入:root = [1,2,2,3,4,4,3]树中节点数目在范围 [1, 1000] 内。(递归栈的深度,h为树的高度)(每个节点访问一次)

2025-08-10 16:48:48 273

原创 LeetCode 热题 100 -- 226. 翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。1.基线条件​​:如果当前节点为 null,直接返回 null。2.递归翻转左右子树​​:先翻转左子树,再翻转右子树。输入:root = [4,2,7,1,3,6,9]3.交换左右子节点​​:将当前节点的左右指针互换。树中节点数目范围在 [0, 100] 内。输出:[4,7,2,9,6,3,1]输入:root = [2,1,3](递归栈的深度,h为树的高度)输入:root = []输出:[2,3,1](每个节点访问一次)

2025-08-10 16:40:53 167

原创 LeetCode 热题 100 --104. 二叉树的最大深度

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。输入:root = [3,9,20,null,null,15,7]给定一个二叉树 root ,返回其最大深度。树中节点的数量在 [0, 104] 区间内。输入:root = [1,null,2](递归栈的深度,h为树的高度)(每个节点访问一次)

2025-08-10 16:33:58 229

原创 LeetCode 热题 100 -- 94. 二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历。输入:root = [1,null,2,3]3.弹出栈顶节点并访问,然后转向其右子节点。树中节点数目在范围 [0, 100] 内。2.从根节点开始,将所有左子节点入栈。输入:root = [1]输入:root = []1.用栈模拟递归过程。输出:[1,3,2]

2025-08-10 16:22:38 214

原创 LeetCode 热题 100 -- 146. LRU 缓存

维护哈希表和双向链表,哈希表的值为原始的key,value为链表节点,链表节点存储原始的key,value。int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1。// 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}// 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}// 缓存是 {1=1, 2=2}// 返回 -1 (未找到)// 返回 -1 (未找到)lRUCache.put(1, 1);// 缓存是 {1=1}

2025-08-09 22:13:19 855

原创 LeetCode 热题 100 -- 23. 合并 K 个升序链表

1.初始化优先队列​​:将每个链表的头节点加入优先队列(最小堆),优先队列会根据节点的值自动排序。输入:lists = [[1,4,5],[1,3,4],[2,6]]​​3.重复步骤2​​:直到优先队列为空,此时所有链表都已合并完成。请你将所有链表合并到一个升序链表中,返回合并后的链表。lists[i].length 的总和不超过 10^4。给你一个链表数组,每个链表都已经按升序排列。输出:[1,1,2,3,4,4,5,6]输入:lists = [[]]输入:lists = []

2025-08-09 20:41:35 371

原创 LeetCode 热题 100 -- 148. 排序链表

subLen:1 -> 2 -> 4 -> 8 …,从1范围内有序,到2范围内有序,到4范围内有序,,自底向上,慢慢地从局部有序扩大到全局有序。​​- 初始化 dummy 节点​​:创建一个虚拟头节点 dummy,用于简化链表操作。​​- 计算链表长度​​:首先遍历链表,计算链表的长度 n。链表中节点的数目在范围 [0, 5 * 104] 内。输入:head = [-1,5,3,4,0]输入:head = [4,2,1,3]输出:[-1,0,3,4,5]输出:[1,2,3,4]输入:head = []

2025-08-09 17:31:44 582

原创 LeetCode 热题 100 -- 138. 随机链表的复制

新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]输入:head = [[3,null],[3,0],[3,null]]输出:[[3,null],[3,0],[3,null]]输入:head = [[1,1],[2,1]]输出:[[1,1],[2,1]]

2025-08-09 10:48:56 209

原创 LeetCode 热题 100 -- 25. K 个一组翻转链表

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。输入:head = [1,2,3,4,5], k = 2。输入:head = [1,2,3,4,5], k = 3。输出:[2,1,4,3,5]输出:[3,2,1,4,5]链表中的节点数目为 n。

2025-08-08 22:53:54 544

原创 Git 在什么情况下会出现代码冲突?如何解决?

并发修改:多人或多个分支修改了相同文件的相同区域。历史分叉:两个分支从同一个提交出发后,各自独立发展,修改了重叠的代码。项目说明何时发生多个分支修改同一文件的同一区域如何识别Git 提示 CONFLICT,文件中出现<<<<<<<标记如何解决手动编辑 → 删除标记 →git add→git commit核心原则人工判断逻辑,确保代码正确性和完整性最佳实践频繁同步、小步提交、使用工具、团队协作✅记住:冲突不是错误,而是 Git 保护代码安全的机制。

2025-08-08 17:24:19 863

原创 @Autowired和@Resource的区别

特性@Autowired@Resource来源Spring 框架默认注入方式按类型(byType)按名称(byName)支持按名称注入需要@Qualifier直接通过name属性是否支持@Primary是否是否支持@Lazy是否可移植性仅限于 Spring 环境跨平台,支持 Java EE 容器推荐使用场景Spring/Spring Boot 项目需要跨平台兼容性的项目。

2025-08-08 17:00:32 1017

原创 Spring,SpringBoot的区别

Spring 和 Spring Boot 是两个紧密相关但功能和目标有所不同的框架。理解它们的区别有助于更好地选择适合项目的工具和技术栈。

2025-08-08 16:50:56 1018

原创 SpringBoot的自动装配机制

自动装配是指 Spring Boot 在启动时根据类路径下的依赖、已定义的 Bean 和其他条件,自动创建并配置应用程序所需的 Bean。这一过程大大减少了开发者手动配置的工作量,提高了开发效率。

2025-08-08 16:15:46 755

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除