打家劫舍 III要点节点是两个状态/** * 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 rob(TreeNode root) { int[] res dfs(root); return Math.max(res[0], res[1]); } private int[] dfs(TreeNode node){ if(node null){ return new int[]{0,0}; } int[] left dfs(node.left); int[] right dfs(node.right); int rob left[1]right[1]node.val; int norob Math.max(left[0], left[1]) Math.max(right[0], right[1]); return new int[]{rob, norob}; } }监控二叉树要点分三种情况讨论int[]class Solution { public int minCameraCover(TreeNode root) { int[] res dfs(root); return Math.min(res[0], res[2]); } private int[] dfs(TreeNode node) { if (node null) { return new int[]{Integer.MAX_VALUE / 2, 0, 0}; // 除 2 防止加法溢出 } int[] left dfs(node.left); int[] right dfs(node.right); int choose Math.min(left[0], left[1]) Math.min(right[0], right[1]) 1; int byFa Math.min(left[0], left[2]) Math.min(right[0], right[2]); int byChildren Math.min(Math.min(left[0] right[2], left[2] right[0]), left[0] right[0]); return new int[]{choose, byFa, byChildren}; } }随机知识核心题**什么是死信队列怎么利用它实现延迟队列面试官为什么这么问死信队列是 RabbitMQ 实现可靠服务的兜底方案延迟队列则是定时任务的异步实现。我问这个是想看你会不会用 RabbitMQ 的特性实现业务需求比如下单未支付自动取消。希望听到怎样的回答死信Dead Letter消息在以下情况会变成死信被消费者 basicReject 或 basicNack 且 requeuefalse。消息 TTL 过期。队列已满无法入队。死信交换机DLX在声明队列时可以指定x-dead-letter-exchange消息成为死信后会被重新路由到指定的死信交换机再进入绑定队列供专门消费者处理记录日志、重试、人工介入。延迟队列实现RabbitMQ 本身没有延迟队列但可以通过TTL DLX组合实现声明一个普通队列设置消息 TTL或者设置队列 TTL。该队列不设消费者消息过期后自动成为死信。死信被路由到真正处理的队列消费者从那里消费就实现了“延迟”消费。结合项目“我们面经 Agent 中用户请求生成一份模拟面试如果大模型服务暂时不可用我们会把请求放入一个带 TTL 的消息如果 30 秒内没被成功消费就自动转入死信通知用户稍后重试。”候选人好的。这个问题分为两部分死信队列的概念和生成条件,以及如何巧妙地用 TTL 死信队列实现延迟队列。我从死信的基本概念讲起,然后说明延迟队列的实现原理,最后结合项目场景举例。第一,消息怎么变成死信。死信其实就是正常队列里待不下去的消息。RabbitMQ 定义了三种情况,消息会被转成死信一是消费者拒绝且不让消息重回队列。消费者收到消息后发现处理不了、格式错误或者重试次数已经耗尽,调basicNack或basicReject返回,同时把requeue设为false。消息被标记为不回去了,变成死信。二是消息 TTL 过期。发送消息时设了存活时间,或者队列设了 TTL 属性,消息在队列里等待超时还没被消费,RabbitMQ 自动判定它过期,成为死信。三是队列满了。队列设置了最大长度,新消息过来时队列已经满了,最老的消息会被挤出去,也变成死信。这三种情况下的死信会被重新交给一个特殊的交换机处理,就是死信交换机(DLX)。声明队列时加参数x-dead-letter-exchange,指定死信的回收站,消息成了死信后,原来的队列把消息原封不动(还会带上一些死信相关的 header)重新路由到死信交换机,再进入绑定的死信队列,等待专门消费者做善后处理——记录异常、发送告警、人工补偿。第二,怎么用死信队列实现延迟队列。RabbitMQ 自身没有直接支持等 30 秒再投递这种延迟队列,但可以通过TTL DLX这个组合巧妙实现。做法是创建两个队列一个是延时队列。这个队列本身不配消费者,只设两个关键属性x-message-ttl指定消息存活时间(或发消息时设expiration),以及x-dead-letter-exchange指向真正的业务交换机。消息进入这个队列后,就静静等待 TTL 到期。一个是业务处理队列。这个队列正常绑定消费者。当延时队列里的消息 TTL 过期、变成死信后,RabbitMQ 自动把这条死信转发到业务交换机,再路由到业务处理队列。消费者从这个业务处理队列拿到消息时,TTL 时间已经过去,达到了延迟消费的效果。举个例子订单 30 分钟未支付自动取消。下单后发消息到延时队列,设 TTL 30 分钟。这 30 分钟内消息搁在延时队列里等过期,时间一到自动转死信,路由到业务处理队列,消费者收到消息后检查订单状态,如果还未支付就执行取消。需要特别注意的是,RabbitMQ 对消息 TTL 的处理是在队列头部检查而非主动轮询每条消息。如果队列是多条不同过期时间的消息混合排列,第一条消息 TTL 未到,后面即使有消息已经过期也不会被处理,因为检查只发生在队列头部。所以建议每个延迟时间独立一个队列,保证同一队列中的所有消息具有统一的 TTL,避免过期消息被头部阻塞。另外,RabbitMQ 3.8 之后也提供了官方的延迟交换机插件rabbitmq-delayed-message-exchange,本质上类似逻辑,只是内部把 TTLDLX 的链路封装好了,使用更简洁。但无论哪种实现,底层都是基于存活时间到期后重新路由的机制。第三,项目中的实际应用。在面试系统里,我们有一个场景用户请求生成模拟面试,此时大模型服务可能正在高负载中,直接返回失败用户体验很差。用延迟队列做如下处理接到生成请求后,先尝试调大模型服务。如果成功,直接返回结果。如果大模型返回繁忙,把请求消息发到延时队列,设 TTL 30 秒。消费者监听的是延时队列对应的业务处理队列。30 秒后,消费者收到消息,再次尝试调用大模型——如果这次成功,正常生成面试结果如果仍然失败,记录异常信息到专门的死信队列,人工介入或通知用户稍后重试。这样的好处是用户请求不会直接丢失,系统会自动在合理时间窗口内重试,同时对大模型形成一定程度的压力缓冲。总结一句话死信队列是消息被拒绝、过期或队列满时的兜底路由机制延迟队列通过 TTL DLX 组合把消息在延时队列里搁一段时间,到期后自动转为死信并路由到真正的处理队列,适用于订单超时、失败重试等需要异步延时的场景。碎碎念后续会更新每天学习的八股和算法 题开始准备秋招的第32天。努力连续更新100天以后每天就按秋招项目【javaagent】科研必做项目算法八股锻炼身体来总结。总结得多动脑子1.算法要系统过一遍【灵神】24/27【早上】1h2.秋招项目【java】开始实际看业务1/6无【agent】还在学整理cc无3.科研要跑一下【下午】3h4.检测项目也得总结文档【晚上】5h6.背八股无7.锻炼身体无杂活2h反思做完东西要总结