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

leecodecode【面试150】【2026.7.2打卡-java版本】

被围绕的区域

要点:bfs

class Solution { public void solve(char[][] board) { //bfs int m = board.length; int n = board[0].length; for(int j = 0; j < n; j++){ if(board[0][j] == 'O'){ bfs(0, j, board); } if(board[m-1][j] == 'O'){ bfs(m-1, j, board); } } for(int i = 0; i < m; i++){ if(board[i][0] == 'O'){ bfs(i,0,board); } if(board[i][n-1] == 'O'){ bfs(i, n-1, board); } } for(int i = 0; i < m; i++){ for(int j = 0; j < n; j++){ //注意顺序 if(board[i][j] == 'O'){ board[i][j] = 'X'; } if(board[i][j] == '#'){ board[i][j] = 'O'; } } } } public void bfs(int i, int j, char[][] board){ //退出的条件 if(i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] == 'X' || board[i][j] == '#'){ return; } board[i][j] = '#'; bfs(i+1, j, board); bfs(i-1, j, board); bfs(i,j+1,board); bfs(i, j-1,board); } }

课程表

要点:建图,入度,bfs

class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { //建图,计算入度,然后找入度为0 的bfs,计算课程 //第一步:建图 List<Integer>[] graph = new ArrayList[numCourses]; for(int i = 0; i < numCourses; i++){ graph[i] = new ArrayList<>(); } //第二步:统计入度 + 完善图的关系 int[] inDegree = new int[numCourses]; for(int[] p : prerequisites){ //这个要修改 int pre = p[1]; int next = p[0]; graph[pre].add(next); inDegree[next]++; } //第三步:找出入读为0的课程 Deque<Integer> queue = new ArrayDeque<>(); for(int i = 0; i < numCourses; i++){ if(inDegree[i] == 0){ queue.offer(i); } } //第四步: 开启上课 bfs int count = 0; while(!queue.isEmpty()){ int current = queue.poll(); count++; for(int nextcourse : graph[current]){ inDegree[nextcourse]--; if(inDegree[nextcourse] == 0){ queue.offer(nextcourse); } } } return count == numCourses; } }

课程表 II

要点:同上,价格返回的数组

class Solution { public int[] findOrder(int numCourses, int[][] prerequisites) { // 1. 建图(邻接表) List<Integer>[] graph = new ArrayList[numCourses]; for (int i = 0; i < numCourses; i++) { graph[i] = new ArrayList<>(); } // 2. 统计入度 + 填充图 int[] inDegree = new int[numCourses]; for (int[] p : prerequisites) { int pre = p[1]; // 先修课 int next = p[0]; // 后修课 graph[pre].add(next); inDegree[next]++; // 后修课的入度 +1 } // 3. 将所有入度为 0 的课程入队(可以直接学) Deque<Integer> queue = new ArrayDeque<>(); for (int i = 0; i < numCourses; i++) { if (inDegree[i] == 0) { queue.offer(i); } } // 4. BFS 拓扑排序,同时记录学习顺序 int[] result = new int[numCourses]; // 存储最终顺序 int index = 0; // 当前填到 result 的第几个位置 while (!queue.isEmpty()) { int current = queue.poll(); result[index++] = current; // 学完这门课,记录顺序 for (int nextCourse : graph[current]) { inDegree[nextCourse]--; // 依赖当前课的课程,前置需求减1 if (inDegree[nextCourse] == 0) { queue.offer(nextCourse); } } } // 5. 如果所有课程都能被安排,返回顺序;否则返回空数组 if (index == numCourses) { return result; } else { return new int[0]; // 有环,无法完成 } } }

随机知识

HashMap JDK1.7 头插法 vs JDK1.8 尾插法完整解析

一、先搞懂:什么是头插、尾插

HashMap 底层数组 + 链表,当哈希冲突时,新元素挂载到链表上:

  1. JDK1.7 头插法新节点直接放在链表头部,原来的链表整体挂在新节点后面。 插入顺序:A→B→C,链表存储顺序:C→B→A(逆序
  2. JDK1.8 尾插法遍历到链表尾部,把新节点接在最后。 插入顺序:A→B→C,链表存储顺序:A→B→C(原顺序保留

二、JDK1.7 为什么设计头插?初衷

设计者理论假设:刚插入的元素,后续被查询的概率更高。 头插后新元素在链表头部,查询时不用遍历整条链表,能更快命中,提升查询效率。 单线程环境下这个逻辑没问题,但完全没考虑多线程并发扩容场景。

三、头插法致命缺陷:并发扩容死循环(CPU100%)

1. 扩容核心逻辑

HashMap 容量满了会触发扩容:新建 2 倍长度数组,把旧数组所有链表节点重新哈希迁移到新数组。 头插迁移规则:每条链表节点从头取出,依次插到新链表头部,迁移后链表反转。

2. 并发下环形链表产生过程(两个线程同时扩容)

假设原链表:A → B,两线程 T1、T2 同时执行resize()扩容

  1. T1 先执行到迁移节点A,刚取出 A,时间片被剥夺暂停;
  2. T2 完整完成扩容:
    • 取出 A 头插 → 新链表 [A]
    • 取出 B 头插 → 新链表 [B→A] 此时内存中B.next = A,A.next = null
  3. T1 恢复继续执行,持有旧节点 A:
    • T1 把 A 头插到新数组,A.next = null
    • 再取旧链表下一个节点 B,把 B 头插,B.next = A
    • 循环读取 B 的下一个节点 A,再次头插,A.next = B
  4. 最终形成环:A ↔ B,环形链表。

3. 死循环现象

后续任何操作(get/put)遍历这条链表时,永远在 A、B 之间无限循环,线程卡死,CPU 占用直接拉满 100%。 除此之外,并发头插还会出现数据丢失、数据重复问题。

四、JDK1.8 尾插法如何彻底规避环形链表

1. 迁移规则改变:保留原有链表顺序

扩容迁移时,整条链表按原顺序复制,节点相对顺序不变,不会反转链表。 原链表A→B,迁移后依然A→B

2. 为什么不会出现环?

尾插是顺序遍历追加,不会颠倒节点指向:

  • 迁移时先完整记录当前链表头、尾节点;
  • 依次把节点接到新链表尾部,节点的 next 指针只单向向后;
  • 不存在 “后插入节点指向前面节点” 的反向指针,永远不可能形成闭环。

多线程同时扩容时,最多出现数据覆盖、丢失(HashMap 本身线程不安全这点没变),不会生成环形链表,彻底杜绝死循环 CPU 打满问题

五、补充两个关键细节

  1. HashMap 本身自始至终都不是线程安全尾插只是修复了并发扩容死循环 bug,多线程场景依然会丢数据、覆盖数据,并发环境必须用ConcurrentHashMap
  2. JDK1.8 不止改了插入方式 冲突链表长度超过 8 会转为红黑树,进一步降低长链表遍历开销,弥补了尾插 “新元素在尾部,查询略慢” 的微小缺陷。

六、一句话总结

  1. JDK1.7 头插:设计初衷是热点数据快速查询,但并发扩容链表反转,极易形成环形链表,死循环 CPU100%;
  2. JDK1.8 尾插:扩容迁移保留链表原有顺序,节点指针单向无反向闭环,彻底解决并发扩容死循环漏洞。

碎碎念:后续会更新每天学习的八股和算法 题,开始准备秋招的第53天。努力连续更新100天!以后每天就按,秋招项目【java +agent】,科研,必做项目,算法,八股,锻炼身体来总结。

总结:慢慢恢复状态吧

1.算法面试150 100/150 2h

2.秋招项目,【java 项目】,

【agent 项目 】,

3.科研要跑一下,

4.实习;6h

6.背八股,1h

7.锻炼身体,

明天试试番茄钟学习法吧

http://www.jsqmd.com/news/1128598/

相关文章:

  • YOLO11改进 - C3k2融合 | C3k2融合CKconv中国结卷积(Chinese Knot Convolution)增强网络对暗弱小目标的特征表达能力 | TGRS 2026
  • 微服务架构下API网关的4种安全方案对比,哪种最适合你的系统?
  • 拼多多推广全攻略高效引流玩法
  • Windows经典游戏联机救星:5分钟让红警、星际、暗黑等老游戏重获新生
  • VLC Android电视版深度配置:打造专业级智能电视媒体中心的7个关键步骤
  • UI自动化测试方案调研:从概念到落地的完整决策指南
  • 为什么内向者会“话题终结者”?
  • 外贸独立站不是门面工程,而是获客引擎
  • 从《中国统计年鉴》到可比数据:手把手教你计算不变价GDP
  • Java程序设计(第3版)第四章——静态代码块
  • Codex + Figma:从零构建高保真 UI 的终极指南
  • Devin工程化落地:AI协作者如何嵌入CI/CD与测试流水线
  • vs调试技巧+宏定义+动态内存
  • 一线老师傅经验谈:选对海绵喷胶源头厂家,粘接寿命延长8年
  • linux x_86_64动态链接,gdb理解link_map参数
  • 内向者和别人聊天缺少共同话题的庖丁解牛
  • 数学艺术图案画-曼陀罗(39)
  • YouTube AI 助手存在提示注入风险,点击链接或致创作者私人视频标题泄露!
  • Dify 本地化部署指南(全平台)
  • 终极精简指南:使用PowerShell脚本让Windows 11瘦身50%
  • 『物流翻译+支付说明多语言』跨境国际化再升级 | VortMall微服务商城系统v1.3.8版本正式发布
  • Claude Code Auto mode 的成本与延迟,别只看模型价格,还要看每一次动作背后的安全往返
  • Audacity终极指南:3小时从零到精通的免费音频编辑完整教程
  • AI让我们啥时候失业。
  • 一站式Android固件解包工具:20+厂商格式的终极解决方案
  • 建站工具测评:BBWEYY/比文云/Framer/Make/Brevo(2026年7月更新)含零代码SAAS、AI编程、源码定制交付
  • Claude Code 会话管理,把一次对话沉淀成可恢复、可分叉、可审计的工程现场
  • Java依赖注入:为何@注解成技术隐患?官方推荐方案揭秘
  • 【学习记录】Week11(三):House of Botcake 与 House of Pig——现代 CTF 堆利用的双子星
  • TC78H653FTG与MK24FN1M0VDC12的直流有刷电机驱动方案