
二叉树的层序遍历要点:队列/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public ListListInteger levelOrder(TreeNode root) { //队列 if(root null){ return new ArrayList(); } DequeTreeNode deque new ArrayDeque(); ListListInteger anss new ArrayList(); deque.offer(root); while(!deque.isEmpty()){ int size deque.size(); ListInteger ans new ArrayList(); for(int i 0; i size; i){ TreeNode temp deque.poll(); ans.add(temp.val); if(temp.left ! null){ deque.offer(temp.left); } if(temp.right ! null){ deque.offer(temp.right); } } anss.add(ans); } return anss; } }二叉树的锯齿形层序遍历要点anss的个数偶数则反转collections.reverse/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public ListListInteger zigzagLevelOrder(TreeNode root) { if(root null){ return new ArrayList(); } DequeTreeNode deque new ArrayDeque(); ListListInteger anss new ArrayList(); //boolean isOuShu false; deque.offer(root); while(!deque.isEmpty()){ int size deque.size(); ListInteger ans new ArrayList(); for(int i 0; i size; i){ TreeNode temp deque.poll(); ans.add(temp.val); if(temp.left ! null){ deque.offer(temp.left); } if(temp.right ! null){ deque.offer(temp.right); } } if(anss.size() % 2 0){ Collections.reverse(ans); } anss.add(ans); } return anss; } }找树左下角的值要点先right再left最后就是了/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val val; * this.left left; * this.right right; * } * } */ class Solution { public int findBottomLeftValue(TreeNode root) { //队列 DequeTreeNode deque new ArrayDeque(); deque.offer(root); TreeNode temp root; while(!deque.isEmpty()){ temp deque.poll(); if(temp.right ! null){ deque.offer(temp.right); } if(temp.left ! null){ deque.offer(temp.left); } } return temp.val; } }随机知识【redis2】二、缓存三剑客必问最高频什么是缓存穿透、缓存击穿、缓存雪崩怎么解决面试官为什么这么问这几乎是 Redis 面试的必考题问烂了仍然要问因为它直接对应线上故障。我要听你非常清晰地用一句话定义每个问题然后给出成熟方案不要含糊。希望听到怎样的回答缓存穿透查询一个根本不存在的数据缓存和 DB 都没有大量请求直接打 DB。解决①布隆过滤器先判断 key 是否存在② 对查询结果为空的情况也缓存设置短的过期时间空值缓存。缓存击穿一个热点 key 过期的瞬间大量请求同时查 DB。解决①互斥锁第一个请求加个锁去查 DB 并写回缓存其他请求等待②逻辑过期value 里带过期时间发现过期就异步重建缓存旧值仍可使用。缓存雪崩大量 key 在同一时间过期或 Redis 宕机请求全部打向 DB。解决① 过期时间加随机值打散过期时间② Redis 集群保证高可用③ 本地缓存或限流做兜底。结合项目“我们的题库缓存会设置随机过期时间避免同一时刻大量题目缓存同时失效。”候选人好的这三个问题是 Redis 缓存使用中最经典的三个风险场景它们都是描述缓存失效导致大量请求直接打到数据库的情况但诱因不同解决方案也不同。我分别说明。第一缓存穿透。缓存穿透是指查询一个数据库中根本不存在的数据。因为缓存里也不会有这条数据所以每次请求都会穿过缓存直接打到数据库上。如果有人恶意用不存在的 ID 大量请求数据库压力会瞬间飙升。解决方案主要有两种一是布隆过滤器。它是一个概率型数据结构特点是它说某个 key 不存在那就一定不存在它说存在可能实际存在也可能不存在有一定误判率。我们把所有可能存在的 key 都预先加载到布隆过滤器里查询前先问布隆过滤器如果返回不存在直接拦截返回空不用查缓存更不用查数据库。二是缓存空值。如果查数据库发现数据确实不存在也把这个空结果缓存起来设置一个短一点的过期时间比如 5 分钟。下次同样的请求来了直接从缓存拿到 null不会再穿透到数据库。但要防止大量不存在的 key 撑爆 Redis 内存所以需要设置较短的 TTL。我项目里对高频查询的接口都加了空值缓存同时记录了异常查询频率如果短时间出现大量不存在的 ID 请求会触发告警人工排查是否被恶意扫描。第二缓存击穿。缓存击穿是指一个热点 key 在过期的瞬间大量并发请求同时打到了数据库。热点 key 比如秒杀商品的库存、爆款文章的详情访问量极高过期时一起重建缓存会把数据库压垮。解决方案也有两种思路一是互斥锁。当缓存失效后不是所有线程都去查数据库重建缓存而是让它们竞争一把分布式锁。拿到锁的线程去查数据库并写回缓存其他线程等待或自旋重试缓存写好后它们直接从缓存拿。这个方案简单可靠缺点是重建期间请求会阻塞。二是逻辑过期。把热点 key 设置成永不过期但在 value 里附加一个逻辑过期时间字段。每次读取时都检查这个字段是否过期如果没有过期直接返回缓存数据如果逻辑时间已过期把旧数据先返回给客户端不等待然后开异步线程去获取互斥锁重建缓存。这避免了请求阻塞用户感知不到延迟但可能在缓存重建完成前的短暂窗口内读到旧数据。项目里的面试题库缓存就用互斥锁防击穿——热门题目缓存过期时只有一个线程去查数据库重建其余线程等待几十毫秒后拿到新缓存。第三缓存雪崩。缓存雪崩是指大量 key 在同一时间过期或者 Redis 服务直接宕机导致海量请求直接打到数据库可能瞬间拖垮整个系统。解决从三个层面下手一过期时间打散。给不同 key 的过期时间加上一个随机值比如 1 到 3 分钟避免集体同时失效。这是最简单有效的手段。二Redis 高可用。采用主从加哨兵或者 Redis Cluster 集群部署单个节点故障时能自动切换减少 Redis 不可用的时间窗口。三多级缓存和限流兜底。在应用层加一层本地缓存比如 CaffeineRedis 挂了还能用本地缓存扛一会同时在网关或服务层加限流保证数据库不会被瞬间流量打死。项目里我们的缓存 key 过期时间都加了随机偏移量同时 Redis 采用主从 哨兵架构保证高可用。对于核心接口还有本地缓存的兜底方案万一 Redis 全部宕机本地缓存和限流可以保证数据库不被打崩。一句话总结穿透是查“不存在”的数据用布隆过滤器和空值缓存防击穿是热点 key 失效瞬间的并发冲击用互斥锁或逻辑过期防雪崩是大面积 key 同时失效或 Redis 宕机用过期时间打散、高可用集群、多级缓存和限流来防。三种问题的本质都是在缓存层和数据库层之间建立缓冲机制保护后端的稳定性。碎碎念后续会更新每天学习的八股和算法 题开始准备秋招的第24天。努力连续更新100天以后每天就按秋招项目【javaagent】科研必做项目算法八股锻炼身体来总结。总结科研要快搞不要三心二意实在不行就听纯音乐1.算法要系统过一遍【灵神】13/27【早上】大概1h2.秋招项目【java】开始实际看业务2.9/10无【agent】还在学helloagent总结2h还是差太多了决定后面看learncc。3.科研要跑一下0.5h新建文件夹4.检测项目也得总结文档3h5.训练项目看看先选择好1h6.背八股7.锻炼身体无反思今天干杂活3h趁现在没事情的话赶快学习